Commit 7940adb1 authored by kasperl@chromium.org's avatar kasperl@chromium.org

Track loop nesting across function calls when the function

is called through an IC the first time.
Review URL: http://codereview.chromium.org/10746

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@764 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 2c680b97
......@@ -1125,6 +1125,7 @@ class FunctionLiteral: public Expression {
start_position_(start_position),
end_position_(end_position),
is_expression_(is_expression),
loop_nesting_(0),
function_token_position_(RelocInfo::kNoPosition) {
}
......@@ -1149,6 +1150,9 @@ class FunctionLiteral: public Expression {
bool AllowsLazyCompilation();
bool loop_nesting() const { return loop_nesting_; }
void set_loop_nesting(int nesting) { loop_nesting_ = nesting; }
private:
Handle<String> name_;
Scope* scope_;
......@@ -1160,6 +1164,7 @@ class FunctionLiteral: public Expression {
int start_position_;
int end_position_;
bool is_expression_;
int loop_nesting_;
int function_token_position_;
};
......
......@@ -296,6 +296,7 @@ class CodeGenerator: public Visitor {
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
Handle<Code> ComputeCallInitialize(int argc);
Handle<Code> ComputeCallInitializeInLoop(int argc);
// Declare global variables and functions in the given array of
// name/value pairs.
......
......@@ -197,6 +197,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
VirtualFrame virtual_frame(this);
frame_ = &virtual_frame;
cc_reg_ = no_condition;
// Adjust for function-level loop nesting.
loop_nesting_ += fun->loop_nesting();
{
CodeGenState state(this);
......@@ -383,11 +387,15 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
}
}
// Adjust for function-level loop nesting.
loop_nesting_ -= fun->loop_nesting();
// Code generation state must be reset.
scope_ = NULL;
frame_ = NULL;
ASSERT(!has_cc());
ASSERT(state_ == NULL);
ASSERT(loop_nesting() == 0);
}
......@@ -2694,14 +2702,15 @@ void CodeGenerator::VisitCall(Call* node) {
// patch the stack to use the global proxy as 'this' in the
// invoked function.
LoadGlobal();
// Load the arguments.
for (int i = 0; i < args->length(); i++) {
Load(args->at(i));
}
// Setup the receiver register and call the IC initialization code.
Handle<Code> stub = ComputeCallInitialize(args->length());
Handle<Code> stub = (loop_nesting() > 0)
? ComputeCallInitializeInLoop(args->length())
: ComputeCallInitialize(args->length());
__ RecordPosition(node->position());
__ call(stub, RelocInfo::CODE_TARGET_CONTEXT);
__ mov(esi, frame_->Context());
......@@ -2745,7 +2754,9 @@ void CodeGenerator::VisitCall(Call* node) {
for (int i = 0; i < args->length(); i++) Load(args->at(i));
// Call the IC initialization code.
Handle<Code> stub = ComputeCallInitialize(args->length());
Handle<Code> stub = (loop_nesting() > 0)
? ComputeCallInitializeInLoop(args->length())
: ComputeCallInitialize(args->length());
__ RecordPosition(node->position());
__ call(stub, RelocInfo::CODE_TARGET);
__ mov(esi, frame_->Context());
......
......@@ -320,6 +320,7 @@ class CodeGenerator: public Visitor {
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
Handle<Code> ComputeCallInitialize(int argc);
Handle<Code> ComputeCallInitializeInLoop(int argc);
// Declare global variables and functions in the given array of
// name/value pairs.
......
......@@ -242,6 +242,15 @@ Handle<Code> CodeGenerator::ComputeCallInitialize(int argc) {
}
Handle<Code> CodeGenerator::ComputeCallInitializeInLoop(int argc) {
// Force the creation of the corresponding stub outside loops,
// because it will be used when clearing the ICs later - when we
// don't know if we're inside a loop or not.
ComputeCallInitialize(argc);
CALL_HEAP_FUNCTION(StubCache::ComputeCallInitializeInLoop(argc), Code);
}
void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
int length = declarations->length();
int globals = 0;
......
......@@ -48,6 +48,7 @@
// CodeGenerator::GenCode
// CodeGenerator::BuildBoilerplate
// CodeGenerator::ComputeCallInitialize
// CodeGenerator::ComputeCallInitializeInLoop
// CodeGenerator::ProcessDeclarations
// CodeGenerator::DeclareGlobals
// CodeGenerator::CheckForInlineRuntimeCall
......
......@@ -239,7 +239,8 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
}
bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared) {
bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
int loop_nesting) {
ZoneScope zone_scope(DELETE_ON_EXIT);
// The VM is in the COMPILER state until exiting this function.
......@@ -271,6 +272,9 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared) {
return false;
}
// Update the loop nesting in the function literal.
lit->set_loop_nesting(loop_nesting);
// Measure how long it takes to do the lazy compilation; only take
// the rest of the function into account to avoid overlap with the
// lazy parsing statistics.
......
......@@ -64,7 +64,7 @@ class Compiler : public AllStatic {
// Compile from function info (used for lazy compilation). Returns
// true on success and false if the compilation resulted in a stack
// overflow.
static bool CompileLazy(Handle<SharedFunctionInfo> shared);
static bool CompileLazy(Handle<SharedFunctionInfo> shared, int loop_nesting);
};
} } // namespace v8::internal
......
......@@ -1135,7 +1135,7 @@ void Debug::ClearStepNext() {
bool Debug::EnsureCompiled(Handle<SharedFunctionInfo> shared) {
if (shared->is_compiled()) return true;
return CompileLazyShared(shared, CLEAR_EXCEPTION);
return CompileLazyShared(shared, CLEAR_EXCEPTION, 0);
}
......
......@@ -282,6 +282,8 @@ typedef void (*InlineCacheCallback)(Code* code, Address ic);
enum InlineCacheState {
// Has never been executed.
UNINITIALIZED,
// Has never been executed, but is in a loop.
UNINITIALIZED_IN_LOOP,
// Has been executed but monomorhic state has been delayed.
PREMONOMORPHIC,
// Has been executed and only one receiver type has been seen.
......
......@@ -422,10 +422,11 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) {
bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
ClearExceptionFlag flag) {
ClearExceptionFlag flag,
int loop_nesting) {
// Compile the source information to a code object.
ASSERT(!shared->is_compiled());
bool result = Compiler::CompileLazy(shared);
bool result = Compiler::CompileLazy(shared, loop_nesting);
ASSERT(result != Top::has_pending_exception());
if (!result && flag == CLEAR_EXCEPTION) Top::clear_pending_exception();
return result;
......@@ -435,10 +436,16 @@ bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag) {
// Compile the source information to a code object.
Handle<SharedFunctionInfo> shared(function->shared());
return CompileLazyShared(shared, flag);
return CompileLazyShared(shared, flag, 0);
}
bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag) {
// Compile the source information to a code object.
Handle<SharedFunctionInfo> shared(function->shared());
return CompileLazyShared(shared, flag, 1);
}
OptimizedObjectForAddingMultipleProperties::
OptimizedObjectForAddingMultipleProperties(Handle<JSObject> object,
bool condition) {
......
......@@ -194,9 +194,13 @@ Handle<Object> SetPrototype(Handle<JSFunction> function,
// Do lazy compilation of the given function. Returns true on success
// and false if the compilation resulted in a stack overflow.
enum ClearExceptionFlag { KEEP_EXCEPTION, CLEAR_EXCEPTION };
bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
ClearExceptionFlag flag);
ClearExceptionFlag flag,
int loop_nesting);
bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag);
bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag);
// These deal with lazily loaded properties.
void SetupLazy(Handle<JSFunction> fun,
......
......@@ -41,6 +41,7 @@ namespace v8 { namespace internal {
static char TransitionMarkFromState(IC::State state) {
switch (state) {
case UNINITIALIZED: return '0';
case UNINITIALIZED_IN_LOOP: return 'L';
case PREMONOMORPHIC: return '0';
case MONOMORPHIC: return '1';
case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
......@@ -223,7 +224,8 @@ void IC::Clear(Address address) {
void CallIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return;
State state = target->ic_state();
if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return;
Code* code = StubCache::FindCallInitialize(target->arguments_count());
SetTargetAtAddress(address, code);
}
......@@ -434,8 +436,9 @@ void CallIC::UpdateCaches(LookupResult* lookup,
if (code->IsFailure()) return;
// Patch the call site depending on the state of the cache.
if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
state == MONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP ||
state == PREMONOMORPHIC || state == MONOMORPHIC ||
state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
}
......@@ -1044,7 +1047,17 @@ Object* CallIC_Miss(Arguments args) {
ASSERT(args.length() == 2);
CallIC ic;
IC::State state = IC::StateFrom(ic.target(), args[0]);
return ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
Object* result =
ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction())
return result;
// Compile the function with the knowledge that it's called from
// within a loop. This enables further optimization of the function.
HandleScope scope;
Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION);
return *function;
}
......
......@@ -4603,6 +4603,7 @@ const char* Code::Kind2String(Kind kind) {
const char* Code::ICState2String(InlineCacheState state) {
switch (state) {
case UNINITIALIZED: return "UNINITIALIZED";
case UNINITIALIZED_IN_LOOP: return "UNINITIALIZED_IN_LOOP";
case PREMONOMORPHIC: return "PREMONOMORPHIC";
case MONOMORPHIC: return "MONOMORPHIC";
case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
......
......@@ -5088,7 +5088,7 @@ static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
if (!done) {
// If the candidate is not compiled compile it to reveal any inner
// functions which might contain the requested source position.
CompileLazyShared(target, KEEP_EXCEPTION);
CompileLazyShared(target, KEEP_EXCEPTION, 0);
}
}
......
......@@ -543,6 +543,17 @@ Object* StubCache::ComputeCallInitialize(int argc) {
}
Object* StubCache::ComputeCallInitializeInLoop(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED_IN_LOOP, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
return FillCache(compiler.CompileCallInitialize(flags));
}
Object* StubCache::ComputeCallPreMonomorphic(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, PREMONOMORPHIC, NORMAL, argc);
......
......@@ -148,6 +148,7 @@ class StubCache : public AllStatic {
// ---
static Object* ComputeCallInitialize(int argc);
static Object* ComputeCallInitializeInLoop(int argc);
static Object* ComputeCallPreMonomorphic(int argc);
static Object* ComputeCallNormal(int argc);
static Object* ComputeCallMegamorphic(int argc);
......
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