Allow closures to be optimized if outer contexts that call eval are all in strict mode.

R=kmillikin@chromium.org
BUG=
TEST=mjsunit/compiler/eval-introduced-closure.js

Review URL: http://codereview.chromium.org/6993008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7853 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ager@chromium.org 2011-05-11 11:26:11 +00:00
parent fa2a92b45f
commit 8a0b1f5bc9
8 changed files with 180 additions and 11 deletions

View File

@ -99,7 +99,7 @@ void CompilationInfo::DisableOptimization() {
FLAG_optimize_closures && FLAG_optimize_closures &&
closure_.is_null() && closure_.is_null() &&
!scope_->HasTrivialOuterContext() && !scope_->HasTrivialOuterContext() &&
!scope_->outer_scope_calls_eval() && !scope_->outer_scope_calls_non_strict_eval() &&
!scope_->inside_with(); !scope_->inside_with();
SetMode(is_optimizable_closure ? BASE : NONOPT); SetMode(is_optimizable_closure ? BASE : NONOPT);
} }

View File

@ -1,4 +1,4 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved. // Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -243,6 +243,27 @@ bool Context::GlobalIfNotShadowedByEval(Handle<String> name) {
} }
void Context::ComputeEvalScopeInfo(bool* outer_scope_calls_eval,
bool* outer_scope_calls_non_strict_eval) {
Context* context = this;
while (true) {
Handle<SerializedScopeInfo> scope_info(
context->closure()->shared()->scope_info());
if (scope_info->CallsEval()) {
*outer_scope_calls_eval = true;
if (!scope_info->IsStrictMode()) {
// No need to go further since the answers will not change
// from here.
*outer_scope_calls_non_strict_eval = true;
return;
}
}
if (context->IsGlobalContext()) break;
context = Context::cast(context->closure()->context());
}
}
void Context::AddOptimizedFunction(JSFunction* function) { void Context::AddOptimizedFunction(JSFunction* function) {
ASSERT(IsGlobalContext()); ASSERT(IsGlobalContext());
#ifdef DEBUG #ifdef DEBUG

View File

@ -351,6 +351,11 @@ class Context: public FixedArray {
// eval. // eval.
bool GlobalIfNotShadowedByEval(Handle<String> name); bool GlobalIfNotShadowedByEval(Handle<String> name);
// Determine if any function scope in the context call eval and if
// any of those calls are in non-strict mode.
void ComputeEvalScopeInfo(bool* outer_scope_calls_eval,
bool* outer_scope_calls_non_strict_eval);
// Code generation support. // Code generation support.
static int SlotOffset(int index) { static int SlotOffset(int index) {
return kHeaderSize + index * kPointerSize - kHeapObjectTag; return kHeaderSize + index * kPointerSize - kHeapObjectTag;

View File

@ -1,4 +1,4 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved. // Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -52,6 +52,7 @@ template<class Allocator>
ScopeInfo<Allocator>::ScopeInfo(Scope* scope) ScopeInfo<Allocator>::ScopeInfo(Scope* scope)
: function_name_(FACTORY->empty_symbol()), : function_name_(FACTORY->empty_symbol()),
calls_eval_(scope->calls_eval()), calls_eval_(scope->calls_eval()),
is_strict_mode_(scope->is_strict_mode()),
parameters_(scope->num_parameters()), parameters_(scope->num_parameters()),
stack_slots_(scope->num_stack_slots()), stack_slots_(scope->num_stack_slots()),
context_slots_(scope->num_heap_slots()), context_slots_(scope->num_heap_slots()),
@ -248,6 +249,7 @@ ScopeInfo<Allocator>::ScopeInfo(SerializedScopeInfo* data)
Object** p = p0; Object** p = p0;
p = ReadSymbol(p, &function_name_); p = ReadSymbol(p, &function_name_);
p = ReadBool(p, &calls_eval_); p = ReadBool(p, &calls_eval_);
p = ReadBool(p, &is_strict_mode_);
p = ReadList<Allocator>(p, &context_slots_, &context_modes_); p = ReadList<Allocator>(p, &context_slots_, &context_modes_);
p = ReadList<Allocator>(p, &parameters_); p = ReadList<Allocator>(p, &parameters_);
p = ReadList<Allocator>(p, &stack_slots_); p = ReadList<Allocator>(p, &stack_slots_);
@ -301,8 +303,8 @@ static Object** WriteList(Object** p,
template<class Allocator> template<class Allocator>
Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() { Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
// function name, calls eval, length for 3 tables: // function name, calls eval, is_strict_mode, length for 3 tables:
const int extra_slots = 1 + 1 + 3; const int extra_slots = 1 + 1 + 1 + 3;
int length = extra_slots + int length = extra_slots +
context_slots_.length() * 2 + context_slots_.length() * 2 +
parameters_.length() + parameters_.length() +
@ -316,6 +318,7 @@ Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
Object** p = p0; Object** p = p0;
p = WriteSymbol(p, function_name_); p = WriteSymbol(p, function_name_);
p = WriteBool(p, calls_eval_); p = WriteBool(p, calls_eval_);
p = WriteBool(p, is_strict_mode_);
p = WriteList(p, &context_slots_, &context_modes_); p = WriteList(p, &context_slots_, &context_modes_);
p = WriteList(p, &parameters_); p = WriteList(p, &parameters_);
p = WriteList(p, &stack_slots_); p = WriteList(p, &stack_slots_);
@ -363,7 +366,8 @@ SerializedScopeInfo* SerializedScopeInfo::Empty() {
Object** SerializedScopeInfo::ContextEntriesAddr() { Object** SerializedScopeInfo::ContextEntriesAddr() {
ASSERT(length() > 0); ASSERT(length() > 0);
return data_start() + 2; // +2 for function name and calls eval. // +3 for function name, calls eval, strict mode.
return data_start() + 3;
} }
@ -392,7 +396,18 @@ bool SerializedScopeInfo::CallsEval() {
p = ReadBool(p, &calls_eval); p = ReadBool(p, &calls_eval);
return calls_eval; return calls_eval;
} }
return true; return false;
}
bool SerializedScopeInfo::IsStrictMode() {
if (length() > 0) {
Object** p = data_start() + 2; // +2 for function name, calls eval.
bool strict_mode;
p = ReadBool(p, &strict_mode);
return strict_mode;
}
return false;
} }

View File

@ -1,4 +1,4 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved. // Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -93,6 +93,7 @@ class ScopeInfo BASE_EMBEDDED {
private: private:
Handle<String> function_name_; Handle<String> function_name_;
bool calls_eval_; bool calls_eval_;
bool is_strict_mode_;
List<Handle<String>, Allocator > parameters_; List<Handle<String>, Allocator > parameters_;
List<Handle<String>, Allocator > stack_slots_; List<Handle<String>, Allocator > stack_slots_;
List<Handle<String>, Allocator > context_slots_; List<Handle<String>, Allocator > context_slots_;
@ -113,6 +114,9 @@ class SerializedScopeInfo : public FixedArray {
// Does this scope call eval? // Does this scope call eval?
bool CallsEval(); bool CallsEval();
// Is this scope a strict mode scope?
bool IsStrictMode();
// Does this scope have an arguments shadow? // Does this scope have an arguments shadow?
bool HasArgumentsShadow() { bool HasArgumentsShadow() {
return StackSlotIndex(GetHeap()->arguments_shadow_symbol()) >= 0; return StackSlotIndex(GetHeap()->arguments_shadow_symbol()) >= 0;

View File

@ -1,4 +1,4 @@
// Copyright 2010 the V8 project authors. All rights reserved. // Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without // Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are // modification, are permitted provided that the following conditions are
// met: // met:
@ -199,6 +199,7 @@ void Scope::SetDefaults(Type type,
// Inherit the strict mode from the parent scope. // Inherit the strict mode from the parent scope.
strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_; strict_mode_ = (outer_scope != NULL) && outer_scope->strict_mode_;
outer_scope_calls_eval_ = false; outer_scope_calls_eval_ = false;
outer_scope_calls_non_strict_eval_ = false;
inner_scope_calls_eval_ = false; inner_scope_calls_eval_ = false;
outer_scope_is_eval_scope_ = false; outer_scope_is_eval_scope_ = false;
force_eager_compilation_ = false; force_eager_compilation_ = false;
@ -483,8 +484,17 @@ void Scope::AllocateVariables(Handle<Context> context) {
// and assume they may invoke eval themselves. Eventually we could capture // and assume they may invoke eval themselves. Eventually we could capture
// this information in the ScopeInfo and then use it here (by traversing // this information in the ScopeInfo and then use it here (by traversing
// the call chain stack, at compile time). // the call chain stack, at compile time).
bool eval_scope = is_eval_scope(); bool eval_scope = is_eval_scope();
PropagateScopeInfo(eval_scope, eval_scope); bool outer_scope_calls_eval = false;
bool outer_scope_calls_non_strict_eval = false;
if (!is_global_scope()) {
context->ComputeEvalScopeInfo(&outer_scope_calls_eval,
&outer_scope_calls_non_strict_eval);
}
PropagateScopeInfo(outer_scope_calls_eval,
outer_scope_calls_non_strict_eval,
eval_scope);
// 2) Resolve variables. // 2) Resolve variables.
Scope* global_scope = NULL; Scope* global_scope = NULL;
@ -616,10 +626,14 @@ void Scope::Print(int n) {
if (HasTrivialOuterContext()) { if (HasTrivialOuterContext()) {
Indent(n1, "// scope has trivial outer context\n"); Indent(n1, "// scope has trivial outer context\n");
} }
if (is_strict_mode()) Indent(n1, "// strict mode scope\n");
if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n"); if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n");
if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n"); if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n");
if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n"); if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
if (outer_scope_calls_eval_) Indent(n1, "// outer scope calls 'eval'\n"); if (outer_scope_calls_eval_) Indent(n1, "// outer scope calls 'eval'\n");
if (outer_scope_calls_non_strict_eval_) {
Indent(n1, "// outer scope calls 'eval' in non-strict context\n");
}
if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n"); if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n");
if (outer_scope_is_eval_scope_) { if (outer_scope_is_eval_scope_) {
Indent(n1, "// outer scope is 'eval' scope\n"); Indent(n1, "// outer scope is 'eval' scope\n");
@ -846,20 +860,30 @@ void Scope::ResolveVariablesRecursively(Scope* global_scope,
bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval, bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval,
bool outer_scope_calls_non_strict_eval,
bool outer_scope_is_eval_scope) { bool outer_scope_is_eval_scope) {
if (outer_scope_calls_eval) { if (outer_scope_calls_eval) {
outer_scope_calls_eval_ = true; outer_scope_calls_eval_ = true;
} }
if (outer_scope_calls_non_strict_eval) {
outer_scope_calls_non_strict_eval_ = true;
}
if (outer_scope_is_eval_scope) { if (outer_scope_is_eval_scope) {
outer_scope_is_eval_scope_ = true; outer_scope_is_eval_scope_ = true;
} }
bool calls_eval = scope_calls_eval_ || outer_scope_calls_eval_; bool calls_eval = scope_calls_eval_ || outer_scope_calls_eval_;
bool is_eval = is_eval_scope() || outer_scope_is_eval_scope_; bool is_eval = is_eval_scope() || outer_scope_is_eval_scope_;
bool calls_non_strict_eval =
(scope_calls_eval_ && !is_strict_mode()) ||
outer_scope_calls_non_strict_eval_;
for (int i = 0; i < inner_scopes_.length(); i++) { for (int i = 0; i < inner_scopes_.length(); i++) {
Scope* inner_scope = inner_scopes_[i]; Scope* inner_scope = inner_scopes_[i];
if (inner_scope->PropagateScopeInfo(calls_eval, is_eval)) { if (inner_scope->PropagateScopeInfo(calls_eval,
calls_non_strict_eval,
is_eval)) {
inner_scope_calls_eval_ = true; inner_scope_calls_eval_ = true;
} }
if (inner_scope->force_eager_compilation_) { if (inner_scope->force_eager_compilation_) {

View File

@ -218,6 +218,9 @@ class Scope: public ZoneObject {
// Information about which scopes calls eval. // Information about which scopes calls eval.
bool calls_eval() const { return scope_calls_eval_; } bool calls_eval() const { return scope_calls_eval_; }
bool outer_scope_calls_eval() const { return outer_scope_calls_eval_; } bool outer_scope_calls_eval() const { return outer_scope_calls_eval_; }
bool outer_scope_calls_non_strict_eval() const {
return outer_scope_calls_non_strict_eval_;
}
// Is this scope inside a with statement. // Is this scope inside a with statement.
bool inside_with() const { return scope_inside_with_; } bool inside_with() const { return scope_inside_with_; }
@ -372,6 +375,7 @@ class Scope: public ZoneObject {
// Computed via PropagateScopeInfo. // Computed via PropagateScopeInfo.
bool outer_scope_calls_eval_; bool outer_scope_calls_eval_;
bool outer_scope_calls_non_strict_eval_;
bool inner_scope_calls_eval_; bool inner_scope_calls_eval_;
bool outer_scope_is_eval_scope_; bool outer_scope_is_eval_scope_;
bool force_eager_compilation_; bool force_eager_compilation_;
@ -400,6 +404,7 @@ class Scope: public ZoneObject {
// Scope analysis. // Scope analysis.
bool PropagateScopeInfo(bool outer_scope_calls_eval, bool PropagateScopeInfo(bool outer_scope_calls_eval,
bool outer_scope_calls_non_strict_eval,
bool outer_scope_is_eval_scope); bool outer_scope_is_eval_scope);
bool HasTrivialContext() const; bool HasTrivialContext() const;

View File

@ -0,0 +1,95 @@
// Copyright 2011 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.
// Test that functions introduced by eval work both when there are
// strict mode and non-strict mode eval in scopes.
// Flags: --allow-natives-syntax
var x = 27;
function f() { return x; }
assertEquals(27, f());
function do_eval(str) {
"use strict";
return eval(str);
}
var eval_f = do_eval('(' + f + ')');
for (var i = 0; i < 5; i++) assertEquals(27, eval_f());
%OptimizeFunctionOnNextCall(eval_f);
assertEquals(27, eval_f());
function do_eval_local(str) {
"use strict";
var x = 42;
return eval(str);
}
eval_f = do_eval_local('(' + f + ')');
for (var i = 0; i < 5; i++) assertEquals(42, eval_f());
%OptimizeFunctionOnNextCall(eval_f);
assertEquals(42, eval_f());
function do_eval_with_other_eval_call(str) {
"use strict";
var f = eval(str);
eval('var x = 1');
return f;
}
eval_f = do_eval_with_other_eval_call('(' + f + ')');
for (var i = 0; i < 5; i++) assertEquals(27, eval_f());
%OptimizeFunctionOnNextCall(eval_f);
assertEquals(27, eval_f());
function test_non_strict_outer_eval() {
function strict_eval(str) { "use strict"; return eval(str); }
var eval_f = strict_eval('(' + f + ')');
for (var i = 0; i < 5; i++) assertEquals(27, eval_f());
%OptimizeFunctionOnNextCall(eval_f);
assertEquals(27, eval_f());
eval("var x = 3");
assertEquals(3, eval_f());
}
test_non_strict_outer_eval();
function test_strict_outer_eval() {
"use strict";
function strict_eval(str) { "use strict"; return eval(str); }
var eval_f = strict_eval('(' + f + ')');
for (var i = 0; i < 5; i++) assertEquals(27, eval_f());
%OptimizeFunctionOnNextCall(eval_f);
assertEquals(27, eval_f());
eval("var x = 3");
assertEquals(27, eval_f());
}
test_non_strict_outer_eval();