Commit 01f7c87f authored by erik.corry@gmail.com's avatar erik.corry@gmail.com

This patch much improves our tracking of whether function is

called from within a loop or not.  In the past we lost the
information if a call site went megamorphic before a lazily
compiled callee was called for the first time.  Now we track
that correctly (this is an issue that affects richards).
We still don't manage to track the in-loop state through a
constructor call, since constructor calls use LoadICs instead
of CallICs.  This issue affects delta-blue.  So in this patch
we assume that lazy compilations that don't happen through a
CallIC happen from inside a loop.  I have an idea to fix this
but this patch is big enough already.
With our improved tracking of in-loop state I have switched
off the inlining of in-object loads for code that is not in
a loop.  This benefits compile speed.  One issue is that
eagerly compiled code now doesn't get the in-object loads
inlined.  We need to eagerly compile less code to fix this.
Review URL: http://codereview.chromium.org/115744

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2046 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 34218e55
......@@ -1058,12 +1058,14 @@ void CodeGenerator::Comparison(Condition cc, bool strict) {
class CallFunctionStub: public CodeStub {
public:
explicit CallFunctionStub(int argc) : argc_(argc) {}
CallFunctionStub(int argc, InLoopFlag in_loop)
: argc_(argc), in_loop_(in_loop) {}
void Generate(MacroAssembler* masm);
private:
int argc_;
InLoopFlag in_loop_;
#if defined(DEBUG)
void Print() { PrintF("CallFunctionStub (argc %d)\n", argc_); }
......@@ -1071,6 +1073,7 @@ class CallFunctionStub: public CodeStub {
Major MajorKey() { return CallFunction; }
int MinorKey() { return argc_; }
InLoopFlag InLoop() { return in_loop_; }
};
......@@ -1088,7 +1091,8 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
CodeForSourcePosition(position);
// Use the shared code stub to call the function.
CallFunctionStub call_function(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub call_function(arg_count, in_loop);
frame_->CallStub(&call_function, arg_count + 1);
// Restore context and pop function from the stack.
......@@ -3051,7 +3055,8 @@ void CodeGenerator::VisitCall(Call* node) {
}
// Setup the receiver register and call the IC initialization code.
Handle<Code> stub = ComputeCallInitialize(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
CodeForSourcePosition(node->position());
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
arg_count + 1);
......@@ -3102,7 +3107,8 @@ void CodeGenerator::VisitCall(Call* node) {
}
// Set the receiver register and call the IC initialization code.
Handle<Code> stub = ComputeCallInitialize(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
CodeForSourcePosition(node->position());
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
......@@ -3200,7 +3206,8 @@ void CodeGenerator::VisitCallEval(CallEval* node) {
// Call the function.
CodeForSourcePosition(node->position());
CallFunctionStub call_function(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub call_function(arg_count, in_loop);
frame_->CallStub(&call_function, arg_count + 1);
__ ldr(cp, frame_->Context());
......@@ -3462,7 +3469,8 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (function == NULL) {
// Call the JS runtime function.
Handle<Code> stub = ComputeCallInitialize(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
frame_->Drop();
......
......@@ -206,6 +206,8 @@ class CodeGenerator: public AstVisitor {
JumpTarget* true_target() const { return state_->true_target(); }
JumpTarget* false_target() const { return state_->false_target(); }
// We don't track loop nesting level on ARM yet.
int loop_nesting() const { return 0; }
// Node visitors.
void VisitStatements(ZoneList<Statement*>* statements);
......@@ -318,8 +320,7 @@ class CodeGenerator: public AstVisitor {
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
Handle<Code> ComputeCallInitialize(int argc);
Handle<Code> ComputeCallInitializeInLoop(int argc);
Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
// Declare global variables and functions in the given array of
// name/value pairs.
......
......@@ -212,7 +212,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Probe the stub cache.
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
StubCache::GenerateProbe(masm, flags, r1, r2, r3);
// If the stub cache probing failed, the receiver might be a value.
......@@ -423,7 +423,9 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
__ ldr(r0, MemOperand(sp, 0));
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
NOT_IN_LOOP,
MONOMORPHIC);
StubCache::GenerateProbe(masm, flags, r0, r2, r3);
// Cache miss: Jump to runtime.
......@@ -756,7 +758,9 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
// Get the receiver from the stack and probe the stub cache.
__ ldr(r1, MemOperand(sp));
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC);
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
NOT_IN_LOOP,
MONOMORPHIC);
StubCache::GenerateProbe(masm, flags, r1, r2, r3);
// Cache miss: Jump to runtime.
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -62,7 +62,7 @@ static void ProbeTable(MacroAssembler* masm,
// Check that the flags match what we're looking for.
__ ldr(offset, FieldMemOperand(offset, Code::kFlagsOffset));
__ and_(offset, offset, Operand(~Code::kFlagsTypeMask));
__ and_(offset, offset, Operand(~Code::kFlagsNotUsedInLookup));
__ cmp(offset, Operand(flags));
__ b(ne, &miss);
......@@ -495,7 +495,9 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
Object* CallStubCompiler::CompileCallField(Object* object,
JSObject* holder,
int index,
String* name) {
String* name,
Code::Flags flags) {
ASSERT_EQ(FIELD, Code::ExtractTypeFromFlags(flags));
// ----------- S t a t e -------------
// -- lr: return address
// -----------------------------------
......@@ -539,14 +541,16 @@ Object* CallStubCompiler::CompileCallField(Object* object,
__ Jump(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(FIELD, name);
return GetCodeWithFlags(flags, name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
CheckType check) {
CheckType check,
Code::Flags flags) {
ASSERT_EQ(CONSTANT_FUNCTION, Code::ExtractTypeFromFlags(flags));
// ----------- S t a t e -------------
// -- lr: return address
// -----------------------------------
......@@ -664,7 +668,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
if (function->shared()->name()->IsString()) {
function_name = String::cast(function->shared()->name());
}
return GetCode(CONSTANT_FUNCTION, function_name);
return GetCodeWithFlags(flags, function_name);
}
......
......@@ -666,12 +666,12 @@ void Builtins::Setup(bool create_heap_objects) {
Code::ComputeFlags(Code::BUILTIN) \
},
#define DEF_FUNCTION_PTR_A(name, kind, state) \
{ FUNCTION_ADDR(Generate_##name), \
NULL, \
#name, \
name, \
Code::ComputeFlags(Code::kind, state) \
#define DEF_FUNCTION_PTR_A(name, kind, state) \
{ FUNCTION_ADDR(Generate_##name), \
NULL, \
#name, \
name, \
Code::ComputeFlags(Code::kind, NOT_IN_LOOP, state) \
},
// Define array of pointers to generators and C builtin functions.
......
......@@ -59,7 +59,7 @@ Handle<Code> CodeStub::GetCode() {
masm.GetCode(&desc);
// Copy the generated code into a heap object, and store the major key.
Code::Flags flags = Code::ComputeFlags(Code::STUB);
Code::Flags flags = Code::ComputeFlags(Code::STUB, InLoop());
Handle<Code> code = Factory::NewCode(desc, NULL, flags, masm.CodeObject());
code->set_major_key(MajorKey());
......
......@@ -83,6 +83,10 @@ class CodeStub BASE_EMBEDDED {
virtual Major MajorKey() = 0;
virtual int MinorKey() = 0;
// The CallFunctionStub needs to override this so it can encode whether a
// lazily generated function should be fully optimized or not.
virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
// Returns a name for logging/debugging purposes.
virtual const char* GetName() { return MajorName(MajorKey()); }
......
......@@ -171,7 +171,8 @@ Handle<Code> CodeGenerator::MakeCode(FunctionLiteral* flit,
CodeDesc desc;
cgen.masm()->GetCode(&desc);
ZoneScopeInfo sinfo(flit->scope());
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION);
InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
Handle<Code> code = Factory::NewCode(desc,
&sinfo,
flags,
......@@ -323,17 +324,18 @@ Handle<JSFunction> CodeGenerator::BuildBoilerplate(FunctionLiteral* node) {
}
Handle<Code> CodeGenerator::ComputeCallInitialize(int argc) {
CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc), Code);
}
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);
Handle<Code> CodeGenerator::ComputeCallInitialize(
int argc,
InLoopFlag in_loop) {
if (in_loop == IN_LOOP) {
// Force the creation of the corresponding stub outside loops,
// because it may be used when clearing the ICs later - it is
// possible for a series of IC transitions to lose the in-loop
// information, and the IC clearing code can't generate a stub
// that it needs so we need to ensure it is generated already.
ComputeCallInitialize(argc, NOT_IN_LOOP);
}
CALL_HEAP_FUNCTION(StubCache::ComputeCallInitialize(argc, in_loop), Code);
}
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -326,8 +326,6 @@ 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.
......@@ -342,6 +340,12 @@ enum InlineCacheState {
};
enum InLoopFlag {
NOT_IN_LOOP,
IN_LOOP
};
// Type of properties.
// Order of properties is significant.
// Must fit in the BitField PropertyDetails::TypeField.
......
......@@ -1653,12 +1653,14 @@ void CodeGenerator::Comparison(Condition cc,
class CallFunctionStub: public CodeStub {
public:
explicit CallFunctionStub(int argc) : argc_(argc) { }
CallFunctionStub(int argc, InLoopFlag in_loop)
: argc_(argc), in_loop_(in_loop) { }
void Generate(MacroAssembler* masm);
private:
int argc_;
InLoopFlag in_loop_;
#ifdef DEBUG
void Print() { PrintF("CallFunctionStub (args %d)\n", argc_); }
......@@ -1666,6 +1668,7 @@ class CallFunctionStub: public CodeStub {
Major MajorKey() { return CallFunction; }
int MinorKey() { return argc_; }
InLoopFlag InLoop() { return in_loop_; }
};
......@@ -1683,7 +1686,8 @@ void CodeGenerator::CallWithArguments(ZoneList<Expression*>* args,
CodeForSourcePosition(position);
// Use the shared code stub to call the function.
CallFunctionStub call_function(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub call_function(arg_count, in_loop);
Result answer = frame_->CallStub(&call_function, arg_count + 1);
// Restore context and replace function on the stack with the
// result of the stub invocation.
......@@ -4217,7 +4221,8 @@ void CodeGenerator::VisitCallEval(CallEval* node) {
// Call the function.
CodeForSourcePosition(node->position());
CallFunctionStub call_function(arg_count);
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub call_function(arg_count, in_loop);
result = frame_->CallStub(&call_function, arg_count + 1);
// Restore the context and overwrite the function on the stack with
......@@ -4581,9 +4586,10 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
}
if (function == NULL) {
// Call the JS runtime function. Pass 0 as the loop nesting depth
// because we do not handle runtime calls specially in loops.
Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, 0);
// Call the JS runtime function.
Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
arg_count,
loop_nesting_);
frame_->RestoreContextRegister();
frame_->SetElementAt(0, &answer);
} else {
......@@ -5395,9 +5401,13 @@ void Reference::GetValue(TypeofState typeof_state) {
bool is_global = var != NULL;
ASSERT(!is_global || var->is_global());
if (is_global || cgen_->scope()->is_global_scope()) {
// Do not inline the inobject property case for loads from the
// global object or loads in toplevel code.
// Do not inline the inobject property case for loads from the global
// object. Also do not inline for unoptimized code. This saves time
// in the code generator. Unoptimized code is toplevel code or code
// that is not in a loop.
if (is_global ||
cgen_->scope()->is_global_scope() ||
cgen_->loop_nesting() == 0) {
Comment cmnt(masm, "[ Load from named Property");
cgen_->frame()->Push(GetName());
......
......@@ -348,7 +348,6 @@ class CodeGenerator: public AstVisitor {
void IncrementLoopNesting() { loop_nesting_++; }
void DecrementLoopNesting() { loop_nesting_--; }
// Node visitors.
void VisitStatements(ZoneList<Statement*>* statements);
......@@ -488,8 +487,7 @@ class CodeGenerator: public AstVisitor {
Handle<JSFunction> BuildBoilerplate(FunctionLiteral* node);
void ProcessDeclarations(ZoneList<Declaration*>* declarations);
Handle<Code> ComputeCallInitialize(int argc);
Handle<Code> ComputeCallInitializeInLoop(int argc);
Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop);
// Declare global variables and functions in the given array of
// name/value pairs.
......
......@@ -427,7 +427,7 @@ void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// Probe the stub cache.
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx);
// If the stub cache probing failed, the receiver might be a value.
......@@ -636,7 +636,9 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
__ mov(eax, Operand(esp, kPointerSize));
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
NOT_IN_LOOP,
MONOMORPHIC);
StubCache::GenerateProbe(masm, flags, eax, ecx, ebx);
// Cache miss: Jump to runtime.
......@@ -839,7 +841,9 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
// Get the receiver from the stack and probe the stub cache.
__ mov(edx, Operand(esp, 4));
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC);
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
NOT_IN_LOOP,
MONOMORPHIC);
StubCache::GenerateProbe(masm, flags, edx, ecx, ebx);
// Cache miss: Jump to runtime.
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -59,7 +59,7 @@ static void ProbeTable(MacroAssembler* masm,
// Check that the flags match what we're looking for.
__ mov(offset, FieldOperand(offset, Code::kFlagsOffset));
__ and_(offset, ~Code::kFlagsTypeMask);
__ and_(offset, ~Code::kFlagsNotUsedInLookup);
__ cmp(offset, flags);
__ j(not_equal, &miss);
......@@ -471,7 +471,9 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
Object* CallStubCompiler::CompileCallField(Object* object,
JSObject* holder,
int index,
String* name) {
String* name,
Code::Flags flags) {
ASSERT_EQ(FIELD, Code::ExtractTypeFromFlags(flags));
// ----------- S t a t e -------------
// -----------------------------------
Label miss;
......@@ -512,14 +514,16 @@ Object* CallStubCompiler::CompileCallField(Object* object,
__ jmp(ic, RelocInfo::CODE_TARGET);
// Return the generated code.
return GetCode(FIELD, name);
return GetCodeWithFlags(flags, name);
}
Object* CallStubCompiler::CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
CheckType check) {
CheckType check,
Code::Flags flags) {
ASSERT_EQ(CONSTANT_FUNCTION, Code::ExtractTypeFromFlags(flags));
// ----------- S t a t e -------------
// -----------------------------------
Label miss;
......@@ -634,7 +638,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
if (function->shared()->name()->IsString()) {
function_name = String::cast(function->shared()->name());
}
return GetCode(CONSTANT_FUNCTION, function_name);
return GetCodeWithFlags(flags, function_name);
}
......
......@@ -941,9 +941,8 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
// Arguments, receiver, and function name are on top of the frame.
// The IC expects them on the stack. It does not drop the function
// name slot (but it does drop the rest).
Handle<Code> ic = (loop_nesting > 0)
? cgen()->ComputeCallInitializeInLoop(arg_count)
: cgen()->ComputeCallInitialize(arg_count);
InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop);
// Spill args, receiver, and function. The call will drop args and
// receiver.
PrepareForCall(arg_count + 2, arg_count + 1);
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -39,10 +39,9 @@ namespace v8 {
namespace internal {
#ifdef DEBUG
static char TransitionMarkFromState(IC::State state) {
static const char TransitionMarkFromState(IC::State state) {
switch (state) {
case UNINITIALIZED: return '0';
case UNINITIALIZED_IN_LOOP: return 'L';
case PREMONOMORPHIC: return 'P';
case MONOMORPHIC: return '1';
case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
......@@ -61,12 +60,14 @@ static char TransitionMarkFromState(IC::State state) {
void IC::TraceIC(const char* type,
Handle<String> name,
State old_state,
Code* new_target) {
Code* new_target,
const char* extra_info) {
if (FLAG_trace_ic) {
State new_state = StateFrom(new_target, Heap::undefined_value());
PrintF("[%s (%c->%c) ", type,
PrintF("[%s (%c->%c)%s", type,
TransitionMarkFromState(old_state),
TransitionMarkFromState(new_state));
TransitionMarkFromState(new_state),
extra_info);
name->Print();
PrintF("]\n");
}
......@@ -227,8 +228,10 @@ void IC::Clear(Address address) {
void CallIC::Clear(Address address, Code* target) {
State state = target->ic_state();
if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return;
Code* code = StubCache::FindCallInitialize(target->arguments_count());
InLoopFlag in_loop = target->ic_in_loop();
if (state == UNINITIALIZED) return;
Code* code =
StubCache::FindCallInitialize(target->arguments_count(), in_loop);
SetTargetAtAddress(address, code);
}
......@@ -391,21 +394,22 @@ void CallIC::UpdateCaches(LookupResult* lookup,
// Compute the number of arguments.
int argc = target()->arguments_count();
InLoopFlag in_loop = target()->ic_in_loop();
Object* code = NULL;
if (state == UNINITIALIZED) {
// This is the first time we execute this inline cache.
// Set the target to the pre monomorphic stub to delay
// setting the monomorphic state.
code = StubCache::ComputeCallPreMonomorphic(argc);
code = StubCache::ComputeCallPreMonomorphic(argc, in_loop);
} else if (state == MONOMORPHIC) {
code = StubCache::ComputeCallMegamorphic(argc);
code = StubCache::ComputeCallMegamorphic(argc, in_loop);
} else {
// Compute monomorphic stub.
switch (lookup->type()) {
case FIELD: {
int index = lookup->GetFieldIndex();
code = StubCache::ComputeCallField(argc, *name, *object,
code = StubCache::ComputeCallField(argc, in_loop, *name, *object,
lookup->holder(), index);
break;
}
......@@ -414,7 +418,7 @@ void CallIC::UpdateCaches(LookupResult* lookup,
// call; used for rewriting to monomorphic state and making sure
// that the code stub is in the stub cache.
JSFunction* function = lookup->GetConstantFunction();
code = StubCache::ComputeCallConstant(argc, *name, *object,
code = StubCache::ComputeCallConstant(argc, in_loop, *name, *object,
lookup->holder(), function);
break;
}
......@@ -426,7 +430,7 @@ void CallIC::UpdateCaches(LookupResult* lookup,
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (lookup->holder() != *receiver) return;
code = StubCache::ComputeCallNormal(argc, *name, *receiver);
code = StubCache::ComputeCallNormal(argc, in_loop, *name, *receiver);
break;
}
case INTERCEPTOR: {
......@@ -444,14 +448,15 @@ void CallIC::UpdateCaches(LookupResult* lookup,
if (code->IsFailure()) return;
// Patch the call site depending on the state of the cache.
if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP ||
state == PREMONOMORPHIC || state == MONOMORPHIC ||
if (state == UNINITIALIZED ||
state == PREMONOMORPHIC ||
state == MONOMORPHIC ||
state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
}
#ifdef DEBUG
TraceIC("CallIC", name, state, target());
TraceIC("CallIC", name, state, target(), in_loop ? " (in-loop)" : "");
#endif
}
......@@ -1089,14 +1094,27 @@ Object* CallIC_Miss(Arguments args) {
IC::State state = IC::StateFrom(ic.target(), args[0]);
Object* result =
ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction())
// The first time the inline cache is updated may be the first time the
// function it references gets called. If the function was lazily compiled
// then the first call will trigger a compilation. We check for this case
// and we do the compilation immediately, instead of waiting for the stub
// currently attached to the JSFunction object to trigger compilation. We
// do this in the case where we know that the inline cache is inside a loop,
// because then we know that we want to optimize the function.
if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
return result;
}
// Compile the function with the knowledge that it's called from
// within a loop. This enables further optimization of the function.
// Compile now with optimization.
HandleScope scope;
Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION);
InLoopFlag in_loop = ic.target()->ic_in_loop();
if (in_loop == IN_LOOP) {
CompileLazyInLoop(function, CLEAR_EXCEPTION);
} else {
CompileLazy(function, CLEAR_EXCEPTION);
}
return *function;
}
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -121,7 +121,8 @@ class IC {
static void TraceIC(const char* type,
Handle<String> name,
State old_state,
Code* new_target);
Code* new_target,
const char* extra_info = "");
#endif
static Failure* TypeError(const char* type,
......
......@@ -1898,6 +1898,11 @@ Code::Kind Code::kind() {
}
InLoopFlag Code::ic_in_loop() {
return ExtractICInLoopFromFlags(flags());
}
InlineCacheState Code::ic_state() {
InlineCacheState result = ExtractICStateFromFlags(flags());
// Only allow uninitialized or debugger states for non-IC code
......@@ -1944,11 +1949,13 @@ bool Code::is_inline_cache_stub() {
Code::Flags Code::ComputeFlags(Kind kind,
InLoopFlag in_loop,
InlineCacheState ic_state,
PropertyType type,
int argc) {
// Compute the bit mask.
int bits = kind << kFlagsKindShift;
if (in_loop) bits |= kFlagsICInLoopMask;
bits |= ic_state << kFlagsICStateShift;
bits |= type << kFlagsTypeShift;
bits |= argc << kFlagsArgumentsCountShift;
......@@ -1956,6 +1963,7 @@ Code::Flags Code::ComputeFlags(Kind kind,
Flags result = static_cast<Flags>(bits);
ASSERT(ExtractKindFromFlags(result) == kind);
ASSERT(ExtractICStateFromFlags(result) == ic_state);
ASSERT(ExtractICInLoopFromFlags(result) == in_loop);
ASSERT(ExtractTypeFromFlags(result) == type);
ASSERT(ExtractArgumentsCountFromFlags(result) == argc);
return result;
......@@ -1964,8 +1972,9 @@ Code::Flags Code::ComputeFlags(Kind kind,
Code::Flags Code::ComputeMonomorphicFlags(Kind kind,
PropertyType type,
InLoopFlag in_loop,
int argc) {
return ComputeFlags(kind, MONOMORPHIC, type, argc);
return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc);
}
......@@ -1981,6 +1990,12 @@ InlineCacheState Code::ExtractICStateFromFlags(Flags flags) {
}
InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) {
int bits = (flags & kFlagsICInLoopMask);
return bits != 0 ? IN_LOOP : NOT_IN_LOOP;
}
PropertyType Code::ExtractTypeFromFlags(Flags flags) {
int bits = (flags & kFlagsTypeMask) >> kFlagsTypeShift;
return static_cast<PropertyType>(bits);
......
......@@ -4860,7 +4860,6 @@ 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";
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -2253,9 +2253,10 @@ class Code: public HeapObject {
// [flags]: Access to specific code flags.
inline Kind kind();
inline InlineCacheState ic_state(); // only valid for IC stubs
inline PropertyType type(); // only valid for monomorphic IC stubs
inline int arguments_count(); // only valid for call IC stubs
inline InlineCacheState ic_state(); // Only valid for IC stubs.
inline InLoopFlag ic_in_loop(); // Only valid for IC stubs..
inline PropertyType type(); // Only valid for monomorphic IC stubs.
inline int arguments_count(); // Only valid for call IC stubs.
// Testers for IC stub kinds.
inline bool is_inline_cache_stub();
......@@ -2277,16 +2278,20 @@ class Code: public HeapObject {
// Flags operations.
static inline Flags ComputeFlags(Kind kind,
InLoopFlag in_loop = NOT_IN_LOOP,
InlineCacheState ic_state = UNINITIALIZED,
PropertyType type = NORMAL,
int argc = -1);
static inline Flags ComputeMonomorphicFlags(Kind kind,
PropertyType type,
int argc = -1);
static inline Flags ComputeMonomorphicFlags(
Kind kind,
PropertyType type,
InLoopFlag in_loop = NOT_IN_LOOP,
int argc = -1);
static inline Kind ExtractKindFromFlags(Flags flags);
static inline InlineCacheState ExtractICStateFromFlags(Flags flags);
static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags);
static inline PropertyType ExtractTypeFromFlags(Flags flags);
static inline int ExtractArgumentsCountFromFlags(Flags flags);
static inline Flags RemoveTypeFromFlags(Flags flags);
......@@ -2378,14 +2383,19 @@ class Code: public HeapObject {
// Flags layout.
static const int kFlagsICStateShift = 0;
static const int kFlagsKindShift = 3;
static const int kFlagsTypeShift = 6;
static const int kFlagsArgumentsCountShift = 9;
static const int kFlagsICStateMask = 0x00000007; // 000000111
static const int kFlagsKindMask = 0x00000038; // 000111000
static const int kFlagsTypeMask = 0x000001C0; // 111000000
static const int kFlagsArgumentsCountMask = 0xFFFFFE00;
static const int kFlagsICInLoopShift = 3;
static const int kFlagsKindShift = 4;
static const int kFlagsTypeShift = 7;
static const int kFlagsArgumentsCountShift = 10;
static const int kFlagsICStateMask = 0x00000007; // 0000000111
static const int kFlagsICInLoopMask = 0x00000008; // 0000001000
static const int kFlagsKindMask = 0x00000070; // 0001110000
static const int kFlagsTypeMask = 0x00000380; // 1110000000
static const int kFlagsArgumentsCountMask = 0xFFFFFC00;
static const int kFlagsNotUsedInLookup =
(kFlagsICInLoopMask | kFlagsTypeMask);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Code);
......
......@@ -4319,9 +4319,15 @@ static Object* Runtime_LazyCompile(Arguments args) {
}
#endif
// Compile the target function.
// Compile the target function. Here we compile using CompileLazyInLoop in
// order to get the optimized version. This helps code like delta-blue
// that calls performance-critical routines through constructors. A
// constructor call doesn't use a CallIC, it uses a LoadIC followed by a
// direct call. Since the in-loop tracking takes place through CallICs
// this means that things called through constructors are never known to
// be in loops. We compile them as if they are in loops here just in case.
ASSERT(!function->is_compiled());
if (!CompileLazy(function, KEEP_EXCEPTION)) {
if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
return Failure::Exception();
}
......
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Copyright 2006-2009 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:
......@@ -370,6 +370,7 @@ Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver,
Object* StubCache::ComputeCallConstant(int argc,
InLoopFlag in_loop,
String* name,
Object* object,
JSObject* holder,
......@@ -388,7 +389,10 @@ Object* StubCache::ComputeCallConstant(int argc,
}
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::CALL_IC, CONSTANT_FUNCTION, argc);
Code::ComputeMonomorphicFlags(Code::CALL_IC,
CONSTANT_FUNCTION,
in_loop,
argc);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
if (object->IsJSObject()) {
......@@ -406,7 +410,7 @@ Object* StubCache::ComputeCallConstant(int argc,
if (!function->is_compiled()) return Failure::InternalError();
// Compile the stub - only create stubs for fully compiled functions.
CallStubCompiler compiler(argc);
code = compiler.CompileCallConstant(object, holder, function, check);
code = compiler.CompileCallConstant(object, holder, function, check, flags);
if (code->IsFailure()) return code;
LOG(CodeCreateEvent("CallIC", Code::cast(code), name));
Object* result = map->UpdateCodeCache(name, Code::cast(code));
......@@ -417,6 +421,7 @@ Object* StubCache::ComputeCallConstant(int argc,
Object* StubCache::ComputeCallField(int argc,
InLoopFlag in_loop,
String* name,
Object* object,
JSObject* holder,
......@@ -431,11 +436,14 @@ Object* StubCache::ComputeCallField(int argc,
object = holder;
}
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, FIELD, argc);
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
FIELD,
in_loop,
argc);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(argc);
code = compiler.CompileCallField(object, holder, index, name);
code = compiler.CompileCallField(object, holder, index, name, flags);
if (code->IsFailure()) return code;
LOG(CodeCreateEvent("CallIC", Code::cast(code), name));
Object* result = map->UpdateCodeCache(name, Code::cast(code));
......@@ -461,7 +469,10 @@ Object* StubCache::ComputeCallInterceptor(int argc,
}
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::CALL_IC, INTERCEPTOR, argc);
Code::ComputeMonomorphicFlags(Code::CALL_IC,
INTERCEPTOR,
NOT_IN_LOOP,
argc);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(argc);
......@@ -476,9 +487,10 @@ Object* StubCache::ComputeCallInterceptor(int argc,
Object* StubCache::ComputeCallNormal(int argc,
InLoopFlag in_loop,
String* name,
JSObject* receiver) {
Object* code = ComputeCallNormal(argc);
Object* code = ComputeCallNormal(argc, in_loop);
if (code->IsFailure()) return code;
return Set(name, receiver->map(), Code::cast(code));
}
......@@ -523,9 +535,9 @@ static Object* FillCache(Object* code) {
}
Code* StubCache::FindCallInitialize(int argc) {
Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
Object* result = ProbeCache(flags);
ASSERT(!result->IsUndefined());
// This might be called during the marking phase of the collector
......@@ -534,9 +546,9 @@ Code* StubCache::FindCallInitialize(int argc) {
}
Object* StubCache::ComputeCallInitialize(int argc) {
Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -544,20 +556,9 @@ Object* StubCache::ComputeCallInitialize(int argc) {
}
Object* StubCache::ComputeCallInitializeInLoop(int argc) {
Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
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);
Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -565,9 +566,9 @@ Object* StubCache::ComputeCallPreMonomorphic(int argc) {
}
Object* StubCache::ComputeCallNormal(int argc) {
Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, MONOMORPHIC, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -575,9 +576,9 @@ Object* StubCache::ComputeCallNormal(int argc) {
}
Object* StubCache::ComputeCallMegamorphic(int argc) {
Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, MEGAMORPHIC, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -587,7 +588,7 @@ Object* StubCache::ComputeCallMegamorphic(int argc) {
Object* StubCache::ComputeCallMiss(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::STUB, MEGAMORPHIC, NORMAL, argc);
Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -598,7 +599,7 @@ Object* StubCache::ComputeCallMiss(int argc) {
#ifdef ENABLE_DEBUGGER_SUPPORT
Object* StubCache::ComputeCallDebugBreak(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, DEBUG_BREAK, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -608,7 +609,11 @@ Object* StubCache::ComputeCallDebugBreak(int argc) {
Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, DEBUG_PREPARE_STEP_IN, NORMAL, argc);
Code::ComputeFlags(Code::CALL_IC,
NOT_IN_LOOP,
DEBUG_PREPARE_STEP_IN,
NORMAL,
argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -619,7 +624,7 @@ Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) {
Object* StubCache::ComputeLazyCompile(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::STUB, UNINITIALIZED, NORMAL, argc);
Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, UNINITIALIZED, NORMAL, argc);
Object* probe = ProbeCache(flags);
if (!probe->IsUndefined()) return probe;
StubCompiler compiler;
......@@ -918,7 +923,10 @@ Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
Object* CallStubCompiler::GetCode(PropertyType type, String* name) {
int argc = arguments_.immediate();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, type, argc);
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
type,
NOT_IN_LOOP,
argc);
return GetCodeWithFlags(flags, name);
}
......
......@@ -128,18 +128,23 @@ class StubCache : public AllStatic {
// ---
static Object* ComputeCallField(int argc,
InLoopFlag in_loop,
String* name,
Object* object,
JSObject* holder,
int index);
static Object* ComputeCallConstant(int argc,
InLoopFlag in_loop,
String* name,
Object* object,
JSObject* holder,
JSFunction* function);
static Object* ComputeCallNormal(int argc, String* name, JSObject* receiver);
static Object* ComputeCallNormal(int argc,
InLoopFlag in_loop,
String* name,
JSObject* receiver);
static Object* ComputeCallInterceptor(int argc,
String* name,
......@@ -148,15 +153,14 @@ 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);
static Object* ComputeCallInitialize(int argc, InLoopFlag in_loop);
static Object* ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop);
static Object* ComputeCallNormal(int argc, InLoopFlag in_loop);
static Object* ComputeCallMegamorphic(int argc, InLoopFlag in_loop);
static Object* ComputeCallMiss(int argc);
// Finds the Code object stored in the Heap::non_monomorphic_cache().
static Code* FindCallInitialize(int argc);
static Code* FindCallInitialize(int argc, InLoopFlag in_loop);
#ifdef ENABLE_DEBUGGER_SUPPORT
static Object* ComputeCallDebugBreak(int argc);
......@@ -209,8 +213,12 @@ class StubCache : public AllStatic {
// 4Gb (and not at all if it isn't).
uint32_t map_low32bits =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map));
// We always set the in_loop bit to zero when generating the lookup code
// so do it here too so the hash codes match.
uint32_t iflags =
(static_cast<uint32_t>(flags) & ~Code::kFlagsNotUsedInLookup);
// Base the offset on a simple combination of name, flags, and map.
uint32_t key = (map_low32bits + field) ^ flags;
uint32_t key = (map_low32bits + field) ^ iflags;
return key & ((kPrimaryTableSize - 1) << kHeapObjectTagSize);
}
......@@ -218,7 +226,11 @@ class StubCache : public AllStatic {
// Use the seed from the primary cache in the secondary cache.
uint32_t string_low32bits =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name));
uint32_t key = seed - string_low32bits + flags;
// We always set the in_loop bit to zero when generating the lookup code
// so do it here too so the hash codes match.
uint32_t iflags =
(static_cast<uint32_t>(flags) & ~Code::kFlagsICInLoopMask);
uint32_t key = seed - string_low32bits + iflags;
return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize);
}
......@@ -469,11 +481,13 @@ class CallStubCompiler: public StubCompiler {
Object* CompileCallField(Object* object,
JSObject* holder,
int index,
String* name);
String* name,
Code::Flags flags);
Object* CompileCallConstant(Object* object,
JSObject* holder,
JSFunction* function,
CheckType check);
CheckType check,
Code::Flags flags);
Object* CompileCallInterceptor(Object* object,
JSObject* holder,
String* name);
......
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