Record function call targets, use them for inlining.

Introduce a version of the CallFunctionStub that records monomorphic
call targets in a one-element cache in the instruction stream.  Use
the cache for inlining attempts in the optimizing backend.

R=fschneider@chromium.org
BUG=
TEST=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9449 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ebc3010c
......@@ -4821,6 +4821,22 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
}
void CallFunctionStub::FinishCode(Code* code) {
code->set_has_function_cache(false);
}
void CallFunctionStub::Clear(Heap* heap, Address address) {
UNREACHABLE();
}
Object* CallFunctionStub::GetCachedValue(Address address) {
UNREACHABLE();
return NULL;
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow, non_function;
......
......@@ -3245,7 +3245,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ Drop(1);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
......
......@@ -764,37 +764,41 @@ bool Call::ComputeGlobalTarget(Handle<GlobalObject> global,
void Call::RecordTypeFeedback(TypeFeedbackOracle* oracle,
CallKind call_kind) {
is_monomorphic_ = oracle->CallIsMonomorphic(this);
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
// Specialize for the receiver types seen at runtime.
Literal* key = property->key()->AsLiteral();
ASSERT(key != NULL && key->handle()->IsString());
Handle<String> name = Handle<String>::cast(key->handle());
receiver_types_.Clear();
oracle->CallReceiverTypes(this, name, call_kind, &receiver_types_);
if (property == NULL) {
// Function call. Specialize for monomorphic calls.
if (is_monomorphic_) target_ = oracle->GetCallTarget(this);
} else {
// Method call. Specialize for the receiver types seen at runtime.
Literal* key = property->key()->AsLiteral();
ASSERT(key != NULL && key->handle()->IsString());
Handle<String> name = Handle<String>::cast(key->handle());
receiver_types_.Clear();
oracle->CallReceiverTypes(this, name, call_kind, &receiver_types_);
#ifdef DEBUG
if (FLAG_enable_slow_asserts) {
int length = receiver_types_.length();
for (int i = 0; i < length; i++) {
Handle<Map> map = receiver_types_.at(i);
ASSERT(!map.is_null() && *map != NULL);
if (FLAG_enable_slow_asserts) {
int length = receiver_types_.length();
for (int i = 0; i < length; i++) {
Handle<Map> map = receiver_types_.at(i);
ASSERT(!map.is_null() && *map != NULL);
}
}
}
#endif
is_monomorphic_ = oracle->CallIsMonomorphic(this);
check_type_ = oracle->GetCallCheckType(this);
if (is_monomorphic_) {
Handle<Map> map;
if (receiver_types_.length() > 0) {
ASSERT(check_type_ == RECEIVER_MAP_CHECK);
map = receiver_types_.at(0);
} else {
ASSERT(check_type_ != RECEIVER_MAP_CHECK);
holder_ = Handle<JSObject>(
oracle->GetPrototypeForPrimitiveCheck(check_type_));
map = Handle<Map>(holder_->map());
check_type_ = oracle->GetCallCheckType(this);
if (is_monomorphic_) {
Handle<Map> map;
if (receiver_types_.length() > 0) {
ASSERT(check_type_ == RECEIVER_MAP_CHECK);
map = receiver_types_.at(0);
} else {
ASSERT(check_type_ != RECEIVER_MAP_CHECK);
holder_ = Handle<JSObject>(
oracle->GetPrototypeForPrimitiveCheck(check_type_));
map = Handle<Map>(holder_->map());
}
is_monomorphic_ = ComputeTarget(map, name);
}
is_monomorphic_ = ComputeTarget(map, name);
}
}
......
......@@ -312,24 +312,20 @@ void KeyedStoreElementStub::Generate(MacroAssembler* masm) {
void ArgumentsAccessStub::PrintName(StringStream* stream) {
const char* type_name = NULL; // Make g++ happy.
stream->Add("ArgumentsAccessStub_");
switch (type_) {
case READ_ELEMENT: type_name = "ReadElement"; break;
case NEW_NON_STRICT_FAST: type_name = "NewNonStrictFast"; break;
case NEW_NON_STRICT_SLOW: type_name = "NewNonStrictSlow"; break;
case NEW_STRICT: type_name = "NewStrict"; break;
case READ_ELEMENT: stream->Add("ReadElement"); break;
case NEW_NON_STRICT_FAST: stream->Add("NewNonStrictFast"); break;
case NEW_NON_STRICT_SLOW: stream->Add("NewNonStrictSlow"); break;
case NEW_STRICT: stream->Add("NewStrict"); break;
}
stream->Add("ArgumentsAccessStub_%s", type_name);
}
void CallFunctionStub::PrintName(StringStream* stream) {
const char* flags_name = NULL; // Make g++ happy.
switch (flags_) {
case NO_CALL_FUNCTION_FLAGS: flags_name = ""; break;
case RECEIVER_MIGHT_BE_IMPLICIT: flags_name = "_Implicit"; break;
}
stream->Add("CallFunctionStub_Args%d%s", argc_, flags_name);
stream->Add("CallFunctionStub_Args%d", argc_);
if (ReceiverMightBeImplicit()) stream->Add("_Implicit");
if (RecordCallTarget()) stream->Add("_Recording");
}
......
......@@ -685,10 +685,32 @@ class CallFunctionStub: public CodeStub {
void Generate(MacroAssembler* masm);
virtual void FinishCode(Code* code);
static void Clear(Heap* heap, Address address);
static Object* GetCachedValue(Address address);
static int ExtractArgcFromMinorKey(int minor_key) {
return ArgcBits::decode(minor_key);
}
// The object that indicates an uninitialized cache.
static Handle<Object> UninitializedSentinel(Isolate* isolate) {
return isolate->factory()->the_hole_value();
}
// A raw version of the uninitialized sentinel that's safe to read during
// garbage collection (e.g., for patching the cache).
static Object* RawUninitializedSentinel(Heap* heap) {
return heap->raw_unchecked_the_hole_value();
}
// The object that indicates a megamorphic state.
static Handle<Object> MegamorphicSentinel(Isolate* isolate) {
return isolate->factory()->undefined_value();
}
private:
int argc_;
CallFunctionFlags flags_;
......@@ -696,8 +718,8 @@ class CallFunctionStub: public CodeStub {
virtual void PrintName(StringStream* stream);
// Minor key encoding in 32 bits with Bitfield <Type, shift, size>.
class FlagBits: public BitField<CallFunctionFlags, 0, 1> {};
class ArgcBits: public BitField<unsigned, 1, 32 - 1> {};
class FlagBits: public BitField<CallFunctionFlags, 0, 2> {};
class ArgcBits: public BitField<unsigned, 2, 32 - 2> {};
Major MajorKey() { return CallFunction; }
int MinorKey() {
......@@ -708,6 +730,10 @@ class CallFunctionStub: public CodeStub {
bool ReceiverMightBeImplicit() {
return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
bool RecordCallTarget() {
return (flags_ & RECORD_CALL_TARGET) != 0;
}
};
......
......@@ -5005,6 +5005,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
}
} else {
expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
VariableProxy* proxy = expr->expression()->AsVariableProxy();
bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
......@@ -5057,6 +5058,46 @@ void HGraphBuilder::VisitCall(Call* expr) {
Drop(argument_count);
}
} else if (expr->IsMonomorphic()) {
// The function is on the stack in the unoptimized code during
// evaluation of the arguments.
CHECK_ALIVE(VisitForValue(expr->expression()));
HValue* function = Top();
HValue* context = environment()->LookupContext();
HGlobalObject* global = new(zone()) HGlobalObject(context);
HGlobalReceiver* receiver = new(zone()) HGlobalReceiver(global);
AddInstruction(global);
PushAndAdd(receiver);
CHECK_ALIVE(VisitExpressions(expr->arguments()));
AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
if (TryInline(expr)) {
// The function is lingering in the deoptimization environment.
// Handle it by case analysis on the AST context.
if (ast_context()->IsEffect()) {
Drop(1);
} else if (ast_context()->IsValue()) {
HValue* result = Pop();
Drop(1);
Push(result);
} else if (ast_context()->IsTest()) {
TestContext* context = TestContext::cast(ast_context());
if (context->if_true()->HasPredecessor()) {
context->if_true()->last_environment()->Drop(1);
}
if (context->if_false()->HasPredecessor()) {
context->if_true()->last_environment()->Drop(1);
}
} else {
UNREACHABLE();
}
return;
} else {
call = PreProcessCall(new(zone()) HInvokeFunction(context,
function,
argument_count));
Drop(1); // The function.
}
} else {
CHECK_ALIVE(VisitArgument(expr->expression()));
HValue* context = environment()->LookupContext();
......
......@@ -4249,25 +4249,49 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
}
void CallFunctionStub::FinishCode(Code* code) {
code->set_has_function_cache(RecordCallTarget());
}
void CallFunctionStub::Clear(Heap* heap, Address address) {
ASSERT(Memory::uint8_at(address + kPointerSize) == Assembler::kTestEaxByte);
// 1 ~ size of the test eax opcode.
Object* cell = Memory::Object_at(address + kPointerSize + 1);
// Low-level because clearing happens during GC.
reinterpret_cast<JSGlobalPropertyCell*>(cell)->set_value(
RawUninitializedSentinel(heap));
}
Object* CallFunctionStub::GetCachedValue(Address address) {
ASSERT(Memory::uint8_at(address + kPointerSize) == Assembler::kTestEaxByte);
// 1 ~ size of the test eax opcode.
Object* cell = Memory::Object_at(address + kPointerSize + 1);
return JSGlobalPropertyCell::cast(cell)->value();
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
Isolate* isolate = masm->isolate();
Label slow, non_function;
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
// function stub.
if (ReceiverMightBeImplicit()) {
Label call;
Label receiver_ok;
// Get the receiver from the stack.
// +1 ~ return address
__ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize));
// Call as function is indicated with the hole.
__ cmp(eax, masm->isolate()->factory()->the_hole_value());
__ j(not_equal, &call, Label::kNear);
__ cmp(eax, isolate->factory()->the_hole_value());
__ j(not_equal, &receiver_ok, Label::kNear);
// Patch the receiver on the stack with the global receiver object.
__ mov(ebx, GlobalObjectOperand());
__ mov(ebx, FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
__ mov(Operand(esp, (argc_ + 1) * kPointerSize), ebx);
__ bind(&call);
__ bind(&receiver_ok);
}
// Get the function to call from the stack.
......@@ -4280,12 +4304,44 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(not_equal, &slow);
if (RecordCallTarget()) {
// Cache the called function in a global property cell in the
// instruction stream after the call. Cache states are uninitialized,
// monomorphic (indicated by a JSFunction), and megamorphic.
Label initialize, call;
// Load the cache cell address into ebx and the cache state into ecx.
__ mov(ebx, Operand(esp, 0)); // Return address.
__ mov(ebx, Operand(ebx, 1)); // 1 ~ sizeof 'test eax' opcode in bytes.
__ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
// A monomorphic cache hit or an already megamorphic state: invoke the
// function without changing the state.
__ cmp(ecx, Operand(edi));
__ j(equal, &call, Label::kNear);
__ cmp(Operand(ecx), Immediate(MegamorphicSentinel(isolate)));
__ j(equal, &call, Label::kNear);
// A monomorphic miss (i.e, here the cache is not uninitialized) goes
// megamorphic.
__ cmp(Operand(ecx), Immediate(UninitializedSentinel(isolate)));
__ j(equal, &initialize, Label::kNear);
__ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
Immediate(MegamorphicSentinel(isolate)));
__ jmp(&call, Label::kNear);
// An uninitialized cache is patched with the function.
__ bind(&initialize);
__ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi);
__ bind(&call);
}
// Fast-case: Just invoke the function.
ParameterCount actual(argc_);
if (ReceiverMightBeImplicit()) {
Label call_as_function;
__ cmp(eax, masm->isolate()->factory()->the_hole_value());
__ cmp(eax, isolate->factory()->the_hole_value());
__ j(equal, &call_as_function);
__ InvokeFunction(edi,
actual,
......@@ -4302,6 +4358,14 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called.
__ bind(&slow);
if (RecordCallTarget()) {
// If there is a call target cache, mark it megamorphic in the
// non-function case.
__ mov(ebx, Operand(esp, 0));
__ mov(ebx, Operand(ebx, 1));
__ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
Immediate(MegamorphicSentinel(isolate)));
}
// Check for function proxy.
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
__ j(not_equal, &non_function);
......@@ -4313,8 +4377,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
__ SetCallKind(ecx, CALL_AS_FUNCTION);
__ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
{
Handle<Code> adaptor =
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
......@@ -4326,8 +4389,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
__ Set(ebx, Immediate(0));
__ SetCallKind(ecx, CALL_AS_METHOD);
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
Handle<Code> adaptor =
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
......
......@@ -2095,8 +2095,29 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
}
// Record source position for debugger.
SetSourcePosition(expr->position());
// Record call targets in unoptimized code, but not in the snapshot.
bool record_call_target = !Serializer::enabled();
if (record_call_target) {
flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
}
CallFunctionStub stub(arg_count, flags);
__ CallStub(&stub);
if (record_call_target) {
// There is a one element cache in the instruction stream.
#ifdef DEBUG
int return_site_offset = masm()->pc_offset();
#endif
Handle<Object> uninitialized =
CallFunctionStub::UninitializedSentinel(isolate());
Handle<JSGlobalPropertyCell> cell =
isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
__ test(eax, Immediate(cell));
// Patching code in the stub assumes the opcode is 1 byte and there is
// word for a pointer in the operand.
ASSERT(masm()->pc_offset() - return_site_offset >= 1 + kPointerSize);
}
RecordJSReturnSite(expr);
// Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
......
......@@ -3096,7 +3096,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ Drop(1);
}
......@@ -3985,9 +3985,16 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
ASSERT(instr->InputAt(0)->IsRegister());
Operand operand = ToOperand(instr->InputAt(0));
__ cmp(operand, instr->hydrogen()->target());
Handle<JSFunction> target = instr->hydrogen()->target();
if (isolate()->heap()->InNewSpace(*target)) {
Register reg = ToRegister(instr->value());
Handle<JSGlobalPropertyCell> cell =
isolate()->factory()->NewJSGlobalPropertyCell(target);
__ cmp(reg, Operand::Cell(cell));
} else {
Operand operand = ToOperand(instr->value());
__ cmp(operand, instr->hydrogen()->target());
}
DeoptimizeIf(not_equal, instr->environment());
}
......
......@@ -1692,7 +1692,13 @@ LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
LOperand* value = UseAtStart(instr->value());
// If the target is in new space, we'll emit a global cell compare and so
// want the value in a register. If the target gets promoted before we
// emit code, we will still get the register but will do an immediate
// compare instead of the cell compare. This is safe.
LOperand* value = Isolate::Current()->heap()->InNewSpace(*instr->target())
? UseRegisterAtStart(instr->value())
: UseAtStart(instr->value());
return AssignEnvironment(new LCheckFunction(value));
}
......
......@@ -1801,6 +1801,8 @@ class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
inputs_[0] = value;
}
LOperand* value() { return inputs_[0]; }
DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
};
......
......@@ -27,6 +27,7 @@
#include "v8.h"
#include "code-stubs.h"
#include "compilation-cache.h"
#include "deoptimizer.h"
#include "execution.h"
......@@ -849,6 +850,12 @@ class StaticMarkingVisitor : public StaticVisitorBase {
// marked since they are contained in HEAP->non_monomorphic_cache().
target = Code::GetCodeFromTargetAddress(rinfo->target_address());
} else {
if (FLAG_cleanup_code_caches_at_gc &&
target->kind() == Code::STUB &&
target->major_key() == CodeStub::CallFunction &&
target->has_function_cache()) {
CallFunctionStub::Clear(heap, rinfo->pc());
}
MarkBit code_mark = Marking::MarkBitFrom(target);
heap->mark_compact_collector()->MarkObject(target, code_mark);
}
......
......@@ -3100,6 +3100,19 @@ void Code::set_to_boolean_state(byte value) {
WRITE_BYTE_FIELD(this, kToBooleanTypeOffset, value);
}
bool Code::has_function_cache() {
ASSERT(kind() == STUB);
return READ_BYTE_FIELD(this, kHasFunctionCacheOffset) != 0;
}
void Code::set_has_function_cache(bool flag) {
ASSERT(kind() == STUB);
WRITE_BYTE_FIELD(this, kHasFunctionCacheOffset, flag);
}
bool Code::is_inline_cache_stub() {
Kind kind = this->kind();
return kind >= FIRST_IC_KIND && kind <= LAST_IC_KIND;
......
......@@ -3716,6 +3716,11 @@ class Code: public HeapObject {
inline byte to_boolean_state();
inline void set_to_boolean_state(byte value);
// For kind STUB, major_key == CallFunction, tells whether there is
// a function cache in the instruction stream.
inline bool has_function_cache();
inline void set_has_function_cache(bool flag);
// Get the safepoint entry for the given pc.
SafepointEntry GetSafepointEntry(Address pc);
......@@ -3855,6 +3860,7 @@ class Code: public HeapObject {
static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kToBooleanTypeOffset = kStubMajorKeyOffset + 1;
static const int kHasFunctionCacheOffset = kStubMajorKeyOffset + 1;
static const int kFullCodeFlags = kOptimizableOffset + 1;
class FullCodeFlagsHasDeoptimizationSupportField:
......
......@@ -76,7 +76,7 @@ Handle<Object> TypeFeedbackOracle::GetInfo(unsigned ast_id) {
bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
......@@ -90,7 +90,7 @@ bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) {
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
Builtins* builtins = Isolate::Current()->builtins();
......@@ -103,7 +103,7 @@ bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) {
bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsMap()) return true;
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
......@@ -116,7 +116,7 @@ bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) {
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
Builtins* builtins = Isolate::Current()->builtins();
......@@ -131,13 +131,13 @@ bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(Expression* expr) {
bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
Handle<Object> value = GetInfo(expr->id());
return value->IsMap() || value->IsSmi();
return value->IsMap() || value->IsSmi() || value->IsJSFunction();
}
Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
ASSERT(LoadIsMonomorphicNormal(expr));
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
Map* first_map = code->FindFirstMap();
......@@ -150,7 +150,7 @@ Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) {
ASSERT(StoreIsMonomorphicNormal(expr));
Handle<Object> map_or_code(GetInfo(expr->id()));
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
return Handle<Map>(code->FindFirstMap());
......@@ -203,6 +203,7 @@ CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
return check;
}
Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
CheckType check) {
JSFunction* function = NULL;
......@@ -225,6 +226,11 @@ Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
}
Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
return Handle<JSFunction>::cast(GetInfo(expr->id()));
}
bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
return *GetInfo(expr->id()) ==
Isolate::Current()->builtins()->builtin(id);
......@@ -488,14 +494,16 @@ void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos,
void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
for (int i = 0; i < infos->length(); i++) {
Address target_address = (*infos)[i].target_address();
unsigned ast_id = static_cast<unsigned>((*infos)[i].data());
Code* target = Code::GetCodeFromTargetAddress((*infos)[i].target_address());
ProcessTarget(ast_id, target);
ProcessTargetAt(target_address, ast_id);
}
}
void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) {
void TypeFeedbackOracle::ProcessTargetAt(Address target_address,
unsigned ast_id) {
Code* target = Code::GetCodeFromTargetAddress(target_address);
switch (target->kind()) {
case Code::LOAD_IC:
case Code::STORE_IC:
......@@ -504,7 +512,7 @@ void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) {
if (target->ic_state() == MONOMORPHIC) {
if (target->kind() == Code::CALL_IC &&
target->check_type() != RECEIVER_MAP_CHECK) {
SetInfo(ast_id, Smi::FromInt(target->check_type()));
SetInfo(ast_id, Smi::FromInt(target->check_type()));
} else {
Object* map = target->FindFirstMap();
SetInfo(ast_id, map == NULL ? static_cast<Object*>(target) : map);
......@@ -529,6 +537,16 @@ void TypeFeedbackOracle::ProcessTarget(unsigned ast_id, Code* target) {
SetInfo(ast_id, target);
break;
case Code::STUB:
if (target->major_key() == CodeStub::CallFunction &&
target->has_function_cache()) {
Object* value = CallFunctionStub::GetCachedValue(target_address);
if (value->IsJSFunction()) {
SetInfo(ast_id, value);
}
}
break;
default:
break;
}
......
......@@ -243,6 +243,8 @@ class TypeFeedbackOracle BASE_EMBEDDED {
CheckType GetCallCheckType(Call* expr);
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
Handle<JSFunction> GetCallTarget(Call* expr);
bool LoadIsBuiltin(Property* expr, Builtins::Name id);
// TODO(1571) We can't use ToBooleanStub::Types as the return value because
......@@ -273,7 +275,7 @@ class TypeFeedbackOracle BASE_EMBEDDED {
byte* old_start,
byte* new_start);
void ProcessRelocInfos(ZoneList<RelocInfo>* infos);
void ProcessTarget(unsigned ast_id, Code* target);
void ProcessTargetAt(Address target_address, unsigned ast_id);
// Returns an element from the backing store. Returns undefined if
// there is no information.
......
......@@ -299,7 +299,9 @@ enum CallFunctionFlags {
NO_CALL_FUNCTION_FLAGS = 0,
// Receiver might implicitly be the global objects. If it is, the
// hole is passed to the call function stub.
RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0
RECEIVER_MIGHT_BE_IMPLICIT = 1 << 0,
// The call target is cached in the instruction stream.
RECORD_CALL_TARGET = 1 << 1
};
......
......@@ -3265,6 +3265,22 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
}
void CallFunctionStub::FinishCode(Code* code) {
code->set_has_function_cache(false);
}
void CallFunctionStub::Clear(Heap* heap, Address address) {
UNREACHABLE();
}
Object* CallFunctionStub::GetCachedValue(Address address) {
UNREACHABLE();
return NULL;
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow, non_function;
......
......@@ -3012,7 +3012,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) {
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ Drop(1);
......
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