Revision: 9449
Author: [email protected]
Date: Tue Sep 27 04:42:02 2011
Log: 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.
[email protected]
BUG=
TEST=
Review URL: http://codereview.chromium.org/7966038
http://code.google.com/p/v8/source/detail?r=9449
Modified:
/branches/bleeding_edge/src/arm/code-stubs-arm.cc
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
/branches/bleeding_edge/src/ast.cc
/branches/bleeding_edge/src/code-stubs.cc
/branches/bleeding_edge/src/code-stubs.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/ia32/code-stubs-ia32.cc
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-ia32.h
/branches/bleeding_edge/src/mark-compact.cc
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/type-info.cc
/branches/bleeding_edge/src/type-info.h
/branches/bleeding_edge/src/v8globals.h
/branches/bleeding_edge/src/x64/code-stubs-x64.cc
/branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
=======================================
--- /branches/bleeding_edge/src/arm/code-stubs-arm.cc Tue Sep 27 03:53:22
2011
+++ /branches/bleeding_edge/src/arm/code-stubs-arm.cc Tue Sep 27 04:42:02
2011
@@ -4819,6 +4819,22 @@
__ bind(&slowcase);
__ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
}
+
+
+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) {
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Mon Sep 26
05:09:04 2011
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Sep 27
04:42:02 2011
@@ -3245,7 +3245,7 @@
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));
=======================================
--- /branches/bleeding_edge/src/ast.cc Mon Sep 19 07:50:33 2011
+++ /branches/bleeding_edge/src/ast.cc Tue Sep 27 04:42:02 2011
@@ -764,37 +764,41 @@
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());
- }
- is_monomorphic_ = ComputeTarget(map, name);
+ 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);
+ }
}
}
=======================================
--- /branches/bleeding_edge/src/code-stubs.cc Thu Sep 22 04:30:04 2011
+++ /branches/bleeding_edge/src/code-stubs.cc Tue Sep 27 04:42:02 2011
@@ -312,24 +312,20 @@
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;
- }
- stream->Add("ArgumentsAccessStub_%s", type_name);
+ 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;
+ }
}
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");
}
=======================================
--- /branches/bleeding_edge/src/code-stubs.h Tue Sep 27 03:53:22 2011
+++ /branches/bleeding_edge/src/code-stubs.h Tue Sep 27 04:42:02 2011
@@ -685,9 +685,31 @@
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_;
@@ -696,8 +718,8 @@
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 @@
bool ReceiverMightBeImplicit() {
return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
+
+ bool RecordCallTarget() {
+ return (flags_ & RECORD_CALL_TARGET) != 0;
+ }
};
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Mon Sep 26 05:20:11 2011
+++ /branches/bleeding_edge/src/hydrogen.cc Tue Sep 27 04:42:02 2011
@@ -5005,6 +5005,7 @@
}
} else {
+ expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION);
VariableProxy* proxy = expr->expression()->AsVariableProxy();
bool global_call = proxy != NULL && proxy->var()->IsUnallocated();
@@ -5056,6 +5057,46 @@
call = new(zone()) HCallGlobal(context, var->name(),
argument_count);
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()));
=======================================
--- /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Tue Sep 27 03:53:22
2011
+++ /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Tue Sep 27 04:42:02
2011
@@ -4247,27 +4247,51 @@
void StackCheckStub::Generate(MacroAssembler* masm) {
__ TailCallRuntime(Runtime::kStackGuard, 0, 1);
}
+
+
+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 @@
__ 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 @@
// 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 @@
__ 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 @@
__ 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);
}
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Thu Sep 22
04:30:04 2011
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Tue Sep 27
04:42:02 2011
@@ -2095,8 +2095,29 @@
}
// 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));
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Mon Sep 26
05:09:04 2011
+++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Tue Sep 27
04:42:02 2011
@@ -3096,7 +3096,7 @@
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::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());
}
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.cc Fri Sep 23 06:28:17
2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.cc Tue Sep 27 04:42:02
2011
@@ -1692,7 +1692,13 @@
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));
}
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-ia32.h Fri Sep 23 06:28:17 2011
+++ /branches/bleeding_edge/src/ia32/lithium-ia32.h Tue Sep 27 04:42:02 2011
@@ -1800,6 +1800,8 @@
explicit LCheckFunction(LOperand* value) {
inputs_[0] = value;
}
+
+ LOperand* value() { return inputs_[0]; }
DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
=======================================
--- /branches/bleeding_edge/src/mark-compact.cc Tue Sep 27 00:52:34 2011
+++ /branches/bleeding_edge/src/mark-compact.cc Tue Sep 27 04:42:02 2011
@@ -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 @@
// 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);
}
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Mon Sep 26 04:14:41 2011
+++ /branches/bleeding_edge/src/objects-inl.h Tue Sep 27 04:42:02 2011
@@ -3099,6 +3099,19 @@
ASSERT(is_to_boolean_ic_stub());
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();
=======================================
--- /branches/bleeding_edge/src/objects.h Mon Sep 26 07:54:57 2011
+++ /branches/bleeding_edge/src/objects.h Tue Sep 27 04:42:02 2011
@@ -3716,6 +3716,11 @@
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 @@
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:
=======================================
--- /branches/bleeding_edge/src/type-info.cc Tue Sep 13 05:53:28 2011
+++ /branches/bleeding_edge/src/type-info.cc Tue Sep 27 04:42:02 2011
@@ -76,7 +76,7 @@
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::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::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::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::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::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());
@@ -202,6 +202,7 @@
ASSERT(check != RECEIVER_MAP_CHECK);
return check;
}
+
Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
CheckType check) {
@@ -223,6 +224,11 @@
ASSERT(function != NULL);
return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
}
+
+
+Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) {
+ return Handle<JSFunction>::cast(GetInfo(expr->id()));
+}
bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
@@ -488,14 +494,16 @@
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 @@
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 @@
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;
}
=======================================
--- /branches/bleeding_edge/src/type-info.h Mon Aug 22 07:23:37 2011
+++ /branches/bleeding_edge/src/type-info.h Tue Sep 27 04:42:02 2011
@@ -243,6 +243,8 @@
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 @@
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.
=======================================
--- /branches/bleeding_edge/src/v8globals.h Mon Sep 26 05:44:36 2011
+++ /branches/bleeding_edge/src/v8globals.h Tue Sep 27 04:42:02 2011
@@ -299,7 +299,9 @@
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
};
=======================================
--- /branches/bleeding_edge/src/x64/code-stubs-x64.cc Tue Sep 27 03:53:22
2011
+++ /branches/bleeding_edge/src/x64/code-stubs-x64.cc Tue Sep 27 04:42:02
2011
@@ -3263,6 +3263,22 @@
void StackCheckStub::Generate(MacroAssembler* masm) {
__ TailCallRuntime(Runtime::kStackGuard, 0, 1);
}
+
+
+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) {
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Mon Sep 26
05:09:04 2011
+++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Tue Sep 27
04:42:02 2011
@@ -3012,7 +3012,7 @@
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);
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev