Revision: 10531
Author: [email protected]
Date: Fri Jan 27 05:03:19 2012
Log: Implement target cache for constructor calls.
This caches call targets of constructor calls by associating one element
caches with call sites. The type feedback oracle can use the recorded
valued to gather type information for monomorphic constructor call sites.
[email protected],[email protected]
Review URL: https://chromiumcodereview.appspot.com/8932004
http://code.google.com/p/v8/source/detail?r=10531
Modified:
/branches/bleeding_edge/src/arm/builtins-arm.cc
/branches/bleeding_edge/src/arm/code-stubs-arm.cc
/branches/bleeding_edge/src/arm/debug-arm.cc
/branches/bleeding_edge/src/arm/full-codegen-arm.cc
/branches/bleeding_edge/src/arm/lithium-codegen-arm.cc
/branches/bleeding_edge/src/builtins.h
/branches/bleeding_edge/src/code-stubs.cc
/branches/bleeding_edge/src/code-stubs.h
/branches/bleeding_edge/src/full-codegen.cc
/branches/bleeding_edge/src/full-codegen.h
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/ia32/assembler-ia32.h
/branches/bleeding_edge/src/ia32/builtins-ia32.cc
/branches/bleeding_edge/src/ia32/code-stubs-ia32.cc
/branches/bleeding_edge/src/ia32/debug-ia32.cc
/branches/bleeding_edge/src/ia32/full-codegen-ia32.cc
/branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
/branches/bleeding_edge/src/mark-compact.cc
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects-visiting-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/x64/builtins-x64.cc
/branches/bleeding_edge/src/x64/code-stubs-x64.cc
/branches/bleeding_edge/src/x64/debug-x64.cc
/branches/bleeding_edge/src/x64/full-codegen-x64.cc
/branches/bleeding_edge/src/x64/lithium-codegen-x64.cc
=======================================
--- /branches/bleeding_edge/src/arm/builtins-arm.cc Thu Jan 26 13:47:57 2012
+++ /branches/bleeding_edge/src/arm/builtins-arm.cc Fri Jan 27 05:03:19 2012
@@ -664,7 +664,9 @@
}
-void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
+static void Generate_JSConstructStubHelper(MacroAssembler* masm,
+ bool is_api_function,
+ bool count_constructions) {
// ----------- S t a t e -------------
// -- r0 : number of arguments
// -- r1 : constructor function
@@ -672,42 +674,6 @@
// -- sp[...]: constructor arguments
// -----------------------------------
- Label slow, non_function_call;
- // Check that the function is not a smi.
- __ JumpIfSmi(r1, &non_function_call);
- // Check that the function is a JSFunction.
- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
- __ b(ne, &slow);
-
- // Jump to the function-specific construct stub.
- __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(r2, FieldMemOperand(r2,
SharedFunctionInfo::kConstructStubOffset));
- __ add(pc, r2, Operand(Code::kHeaderSize - kHeapObjectTag));
-
- // r0: number of arguments
- // r1: called object
- // r2: object type
- Label do_call;
- __ bind(&slow);
- __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
- __ b(ne, &non_function_call);
- __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
- __ jmp(&do_call);
-
- __ bind(&non_function_call);
- __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
- __ bind(&do_call);
- // Set expected number of arguments to zero (not changing r0).
- __ mov(r2, Operand(0, RelocInfo::NONE));
- __ SetCallKind(r5, CALL_AS_METHOD);
- __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
- RelocInfo::CODE_TARGET);
-}
-
-
-static void Generate_JSConstructStubHelper(MacroAssembler* masm,
- bool is_api_function,
- bool count_constructions) {
// Should never count constructions for api objects.
ASSERT(!is_api_function || !count_constructions);
@@ -1114,7 +1080,8 @@
// Invoke the code and pass argc as r0.
__ mov(r0, Operand(r3));
if (is_construct) {
- __ Call(masm->isolate()->builtins()->JSConstructCall());
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ CallStub(&stub);
} else {
ParameterCount actual(r0);
__ InvokeFunction(r1, actual, CALL_FUNCTION,
=======================================
--- /branches/bleeding_edge/src/arm/code-stubs-arm.cc Thu Jan 26 08:05:28
2012
+++ /branches/bleeding_edge/src/arm/code-stubs-arm.cc Fri Jan 27 05:03:19
2012
@@ -5124,24 +5124,49 @@
}
-void CallFunctionStub::FinishCode(Handle<Code> code) {
- code->set_has_function_cache(false);
-}
-
-
-void CallFunctionStub::Clear(Heap* heap, Address address) {
- UNREACHABLE();
-}
-
-
-Object* CallFunctionStub::GetCachedValue(Address address) {
- UNREACHABLE();
- return NULL;
+static void GenerateRecordCallTarget(MacroAssembler* masm) {
+ // Cache the called function in a global property cell. Cache states
+ // are uninitialized, monomorphic (indicated by a JSFunction), and
+ // megamorphic.
+ // r1 : the function to call
+ // r2 : cache cell for call target
+ Isolate* isolate = masm->isolate();
+ Label done;
+
+ ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(isolate),
+ isolate->heap()->undefined_value());
+ ASSERT_EQ(*TypeFeedbackCells::UninitializedSentinel(isolate),
+ isolate->heap()->the_hole_value());
+
+ // Load the cache state into r3.
+ __ ldr(r3, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmp(r3, r1);
+ __ b(eq, &done);
+ __ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
+ __ b(eq, &done);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex, ne);
+ __ str(ip, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset), ne);
+
+ // An uninitialized cache is patched with the function.
+ __ str(r1, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset), eq);
+ // No need for a write barrier here - cells are rescanned.
+
+ __ bind(&done);
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
// r1 : the function to call
+ // r2 : cache cell for call target
Label slow, non_function;
// The receiver might implicitly be the global object. This is
@@ -5217,6 +5242,48 @@
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
}
+
+
+void CallConstructStub::Generate(MacroAssembler* masm) {
+ // r0 : number of arguments
+ // r1 : the function to call
+ // r2 : cache cell for call target
+ Label slow, non_function_call;
+
+ // Check that the function is not a smi.
+ __ JumpIfSmi(r1, &non_function_call);
+ // Check that the function is a JSFunction.
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
+ __ b(ne, &slow);
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Jump to the function-specific construct stub.
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r2, FieldMemOperand(r2,
SharedFunctionInfo::kConstructStubOffset));
+ __ add(pc, r2, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+ // r0: number of arguments
+ // r1: called object
+ // r3: object type
+ Label do_call;
+ __ bind(&slow);
+ __ cmp(r3, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ b(ne, &non_function_call);
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
+ __ jmp(&do_call);
+
+ __ bind(&non_function_call);
+ __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ bind(&do_call);
+ // Set expected number of arguments to zero (not changing r0).
+ __ mov(r2, Operand(0, RelocInfo::NONE));
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
// Unfortunately you have to run without snapshots to see most of these
=======================================
--- /branches/bleeding_edge/src/arm/debug-arm.cc Tue Nov 8 06:39:37 2011
+++ /branches/bleeding_edge/src/arm/debug-arm.cc Fri Jan 27 05:03:19 2012
@@ -255,7 +255,8 @@
// Calling convention for construct call (from builtins-arm.cc)
// -- r0 : number of arguments (not smi)
// -- r1 : constructor function
- Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit());
+ // -- r2 : cache cell for call target
+ Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit(), r0.bit());
}
@@ -268,6 +269,7 @@
void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
+ // Register state for CallFunctionStub (from code-stubs-arm.cc).
// ----------- S t a t e -------------
// -- r1 : function
// -----------------------------------
=======================================
--- /branches/bleeding_edge/src/arm/full-codegen-arm.cc Thu Jan 26 08:05:28
2012
+++ /branches/bleeding_edge/src/arm/full-codegen-arm.cc Fri Jan 27 05:03:19
2012
@@ -2379,9 +2379,22 @@
__ mov(r0, Operand(arg_count));
__ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
- Handle<Code> construct_builtin =
- isolate()->builtins()->JSConstructCall();
- __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
+ // Record call targets in unoptimized code, but not in the snapshot.
+ CallFunctionFlags flags;
+ if (!Serializer::enabled()) {
+ flags = RECORD_CALL_TARGET;
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<JSGlobalPropertyCell> cell =
+ isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
+ RecordTypeFeedbackCell(expr->id(), cell);
+ __ mov(r2, Operand(cell));
+ } else {
+ flags = NO_CALL_FUNCTION_FLAGS;
+ }
+
+ CallConstructStub stub(flags);
+ __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
context()->Plug(r0);
}
=======================================
--- /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Tue Jan 24
00:43:12 2012
+++ /branches/bleeding_edge/src/arm/lithium-codegen-arm.cc Fri Jan 27
05:03:19 2012
@@ -3376,9 +3376,9 @@
ASSERT(ToRegister(instr->InputAt(0)).is(r1));
ASSERT(ToRegister(instr->result()).is(r0));
- Handle<Code> builtin = isolate()->builtins()->JSConstructCall();
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
__ mov(r0, Operand(instr->arity()));
- CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr);
+ CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr);
}
=======================================
--- /branches/bleeding_edge/src/builtins.h Fri Jan 13 05:09:52 2012
+++ /branches/bleeding_edge/src/builtins.h Fri Jan 27 05:03:19 2012
@@ -67,8 +67,6 @@
#define BUILTIN_LIST_A(V) \
V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \
Code::kNoExtraICState) \
- V(JSConstructCall, BUILTIN, UNINITIALIZED, \
- Code::kNoExtraICState) \
V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \
Code::kNoExtraICState) \
V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \
@@ -346,7 +344,6 @@
static void Generate_Adaptor(MacroAssembler* masm,
CFunctionId id,
BuiltinExtraArguments extra_args);
- static void Generate_JSConstructCall(MacroAssembler* masm);
static void Generate_JSConstructStubCountdown(MacroAssembler* masm);
static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
static void Generate_JSConstructStubApi(MacroAssembler* masm);
=======================================
--- /branches/bleeding_edge/src/code-stubs.cc Mon Jan 16 01:44:35 2012
+++ /branches/bleeding_edge/src/code-stubs.cc Fri Jan 27 05:03:19 2012
@@ -340,6 +340,12 @@
if (ReceiverMightBeImplicit()) stream->Add("_Implicit");
if (RecordCallTarget()) stream->Add("_Recording");
}
+
+
+void CallConstructStub::PrintName(StringStream* stream) {
+ stream->Add("CallConstructStub");
+ if (RecordCallTarget()) stream->Add("_Recording");
+}
void ToBooleanStub::PrintName(StringStream* stream) {
=======================================
--- /branches/bleeding_edge/src/code-stubs.h Fri Dec 9 01:26:14 2011
+++ /branches/bleeding_edge/src/code-stubs.h Fri Jan 27 05:03:19 2012
@@ -38,6 +38,7 @@
// List of code stubs used on all platforms.
#define CODE_STUB_LIST_ALL_PLATFORMS(V) \
V(CallFunction) \
+ V(CallConstruct) \
V(UnaryOp) \
V(BinaryOp) \
V(StringAdd) \
@@ -738,31 +739,9 @@
void Generate(MacroAssembler* masm);
- virtual void FinishCode(Handle<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_;
@@ -783,6 +762,26 @@
bool ReceiverMightBeImplicit() {
return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
+
+ bool RecordCallTarget() {
+ return (flags_ & RECORD_CALL_TARGET) != 0;
+ }
+};
+
+
+class CallConstructStub: public CodeStub {
+ public:
+ explicit CallConstructStub(CallFunctionFlags flags) : flags_(flags) {}
+
+ void Generate(MacroAssembler* masm);
+
+ private:
+ CallFunctionFlags flags_;
+
+ virtual void PrintName(StringStream* stream);
+
+ Major MajorKey() { return CallConstruct; }
+ int MinorKey() { return flags_; }
bool RecordCallTarget() {
return (flags_ & RECORD_CALL_TARGET) != 0;
=======================================
--- /branches/bleeding_edge/src/full-codegen.cc Mon Jan 23 17:46:26 2012
+++ /branches/bleeding_edge/src/full-codegen.cc Fri Jan 27 05:03:19 2012
@@ -285,6 +285,7 @@
Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
code->set_optimizable(info->IsOptimizable());
cgen.PopulateDeoptimizationData(code);
+ cgen.PopulateTypeFeedbackCells(code);
code->set_has_deoptimization_support(info->HasDeoptimizationSupport());
code->set_handler_table(*cgen.handler_table());
#ifdef ENABLE_DEBUGGER_SUPPORT
@@ -329,8 +330,7 @@
ASSERT(info_->HasDeoptimizationSupport() || bailout_entries_.is_empty());
if (!info_->HasDeoptimizationSupport()) return;
int length = bailout_entries_.length();
- Handle<DeoptimizationOutputData> data =
- isolate()->factory()->
+ Handle<DeoptimizationOutputData> data = isolate()->factory()->
NewDeoptimizationOutputData(length, TENURED);
for (int i = 0; i < length; i++) {
data->SetAstId(i, Smi::FromInt(bailout_entries_[i].id));
@@ -340,6 +340,21 @@
}
+void FullCodeGenerator::PopulateTypeFeedbackCells(Handle<Code> code) {
+ if (type_feedback_cells_.is_empty()) return;
+ int length = type_feedback_cells_.length();
+ int array_size = TypeFeedbackCells::LengthOfFixedArray(length);
+ Handle<TypeFeedbackCells> cache = Handle<TypeFeedbackCells>::cast(
+ isolate()->factory()->NewFixedArray(array_size, TENURED));
+ for (int i = 0; i < length; i++) {
+ cache->SetAstId(i, Smi::FromInt(type_feedback_cells_[i].ast_id));
+ cache->SetCell(i, *type_feedback_cells_[i].cell);
+ }
+ code->set_type_feedback_cells(*cache);
+}
+
+
+
void FullCodeGenerator::PrepareForBailout(Expression* node, State state) {
PrepareForBailoutForId(node->id(), state);
}
@@ -383,6 +398,13 @@
#endif // DEBUG
bailout_entries_.Add(entry);
}
+
+
+void FullCodeGenerator::RecordTypeFeedbackCell(
+ unsigned id, Handle<JSGlobalPropertyCell> cell) {
+ TypeFeedbackCellEntry entry = { id, cell };
+ type_feedback_cells_.Add(entry);
+}
void FullCodeGenerator::RecordStackCheck(unsigned ast_id) {
=======================================
--- /branches/bleeding_edge/src/full-codegen.h Fri Jan 20 08:17:08 2012
+++ /branches/bleeding_edge/src/full-codegen.h Fri Jan 27 05:03:19 2012
@@ -85,13 +85,15 @@
loop_depth_(0),
context_(NULL),
bailout_entries_(0),
- stack_checks_(2) { // There's always at least one.
+ stack_checks_(2), // There's always at least one.
+ type_feedback_cells_(0) {
}
static bool MakeCode(CompilationInfo* info);
void Generate(CompilationInfo* info);
void PopulateDeoptimizationData(Handle<Code> code);
+ void PopulateTypeFeedbackCells(Handle<Code> code);
Handle<FixedArray> handler_table() { return handler_table_; }
@@ -394,6 +396,10 @@
void PrepareForBailout(Expression* node, State state);
void PrepareForBailoutForId(unsigned id, State state);
+ // Cache cell support. This associates AST ids with global property
cells
+ // that will be cleared during GC and collected by the type-feedback
oracle.
+ void RecordTypeFeedbackCell(unsigned id, Handle<JSGlobalPropertyCell>
cell);
+
// Record a call's return site offset, used to rebuild the frame if the
// called function was inlined at the site.
void RecordJSReturnSite(Call* call);
@@ -573,6 +579,11 @@
unsigned pc_and_state;
};
+ struct TypeFeedbackCellEntry {
+ unsigned ast_id;
+ Handle<JSGlobalPropertyCell> cell;
+ };
+
class ExpressionContext BASE_EMBEDDED {
public:
@@ -759,6 +770,7 @@
const ExpressionContext* context_;
ZoneList<BailoutEntry> bailout_entries_;
ZoneList<BailoutEntry> stack_checks_;
+ ZoneList<TypeFeedbackCellEntry> type_feedback_cells_;
Handle<FixedArray> handler_table_;
friend class NestedStatement;
=======================================
--- /branches/bleeding_edge/src/heap.cc Thu Jan 26 13:47:57 2012
+++ /branches/bleeding_edge/src/heap.cc Fri Jan 27 05:03:19 2012
@@ -3339,6 +3339,8 @@
code->set_check_type(RECEIVER_MAP_CHECK);
}
code->set_deoptimization_data(empty_fixed_array(), SKIP_WRITE_BARRIER);
+
code->set_type_feedback_cells(TypeFeedbackCells::cast(empty_fixed_array()),
+ SKIP_WRITE_BARRIER);
code->set_handler_table(empty_fixed_array(), SKIP_WRITE_BARRIER);
code->set_gc_metadata(Smi::FromInt(0));
// Allow self references to created code object by patching the handle to
=======================================
--- /branches/bleeding_edge/src/ia32/assembler-ia32.h Tue Dec 6 04:11:08
2011
+++ /branches/bleeding_edge/src/ia32/assembler-ia32.h Fri Jan 27 05:03:19
2012
@@ -621,8 +621,6 @@
// The debug break slot must be able to contain a call instruction.
static const int kDebugBreakSlotLength = kCallInstructionLength;
- // One byte opcode for test eax,0xXXXXXXXX.
- static const byte kTestEaxByte = 0xA9;
// One byte opcode for test al, 0xXX.
static const byte kTestAlByte = 0xA8;
// One byte opcode for nop.
=======================================
--- /branches/bleeding_edge/src/ia32/builtins-ia32.cc Thu Jan 26 13:47:57
2012
+++ /branches/bleeding_edge/src/ia32/builtins-ia32.cc Fri Jan 27 05:03:19
2012
@@ -74,50 +74,14 @@
}
-void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
+static void Generate_JSConstructStubHelper(MacroAssembler* masm,
+ bool is_api_function,
+ bool count_constructions) {
// ----------- S t a t e -------------
// -- eax: number of arguments
// -- edi: constructor function
// -----------------------------------
- Label slow, non_function_call;
- // Check that function is not a smi.
- __ JumpIfSmi(edi, &non_function_call);
- // Check that function is a JSFunction.
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &slow);
-
- // Jump to the function-specific construct stub.
- __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
- __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
- __ jmp(ebx);
-
- // edi: called object
- // eax: number of arguments
- // ecx: object map
- Label do_call;
- __ bind(&slow);
- __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
- __ j(not_equal, &non_function_call);
- __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
- __ jmp(&do_call);
-
- __ bind(&non_function_call);
- __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
- __ bind(&do_call);
- // Set expected number of arguments to zero (not changing eax).
- __ Set(ebx, Immediate(0));
- Handle<Code> arguments_adaptor =
- masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
- __ SetCallKind(ecx, CALL_AS_METHOD);
- __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
-}
-
-
-static void Generate_JSConstructStubHelper(MacroAssembler* masm,
- bool is_api_function,
- bool count_constructions) {
// Should never count constructions for api objects.
ASSERT(!is_api_function || !count_constructions);
@@ -454,8 +418,8 @@
// Invoke the code.
if (is_construct) {
- __ call(masm->isolate()->builtins()->JSConstructCall(),
- RelocInfo::CODE_TARGET);
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ CallStub(&stub);
} else {
ParameterCount actual(eax);
__ InvokeFunction(edi, actual, CALL_FUNCTION,
=======================================
--- /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Thu Jan 19 06:43:25
2012
+++ /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Fri Jan 27 05:03:19
2012
@@ -4573,30 +4573,46 @@
}
-void CallFunctionStub::FinishCode(Handle<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();
+static void GenerateRecordCallTarget(MacroAssembler* masm) {
+ // Cache the called function in a global property cell. Cache states
+ // are uninitialized, monomorphic (indicated by a JSFunction), and
+ // megamorphic.
+ // ebx : cache cell for call target
+ // edi : the function to call
+ Isolate* isolate = masm->isolate();
+ Label initialize, done;
+
+ // Load the cache state into ecx.
+ __ mov(ecx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmp(ecx, edi);
+ __ j(equal, &done, Label::kNear);
+ __ cmp(ecx, Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
+ __ j(equal, &done, Label::kNear);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ cmp(ecx,
Immediate(TypeFeedbackCells::UninitializedSentinel(isolate)));
+ __ j(equal, &initialize, Label::kNear);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
+ Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
+ __ jmp(&done, Label::kNear);
+
+ // An uninitialized cache is patched with the function.
+ __ bind(&initialize);
+ __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset), edi);
+ // No need for a write barrier here - cells are rescanned.
+
+ __ bind(&done);
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
+ // ebx : cache cell for call target
// edi : the function to call
Isolate* isolate = masm->isolate();
Label slow, non_function;
@@ -4613,9 +4629,9 @@
__ 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);
+ __ mov(ecx, GlobalObjectOperand());
+ __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), ecx);
__ bind(&receiver_ok);
}
@@ -4626,38 +4642,7 @@
__ 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, edi);
- __ j(equal, &call, Label::kNear);
- __ cmp(ecx, Immediate(MegamorphicSentinel(isolate)));
- __ j(equal, &call, Label::kNear);
-
- // A monomorphic miss (i.e, here the cache is not uninitialized) goes
- // megamorphic.
- __ cmp(ecx, Immediate(UninitializedSentinel(isolate)));
- __ j(equal, &initialize, Label::kNear);
- // MegamorphicSentinel is an immortal immovable object (undefined) so
no
- // write-barrier is needed.
- __ 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);
- // No need for a write barrier here - cells are rescanned.
-
- __ bind(&call);
+ GenerateRecordCallTarget(masm);
}
// Fast-case: Just invoke the function.
@@ -4684,13 +4669,10 @@
__ 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));
- // MegamorphicSentinel is an immortal immovable object (undefined) so
no
- // write barrier is needed.
+ // non-function case. MegamorphicSentinel is an immortal immovable
+ // object (undefined) so no write barrier is needed.
__ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
- Immediate(MegamorphicSentinel(isolate)));
+ Immediate(TypeFeedbackCells::MegamorphicSentinel(isolate)));
}
// Check for function proxy.
__ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
@@ -4718,6 +4700,50 @@
Handle<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
+
+
+void CallConstructStub::Generate(MacroAssembler* masm) {
+ // eax : number of arguments
+ // ebx : cache cell for call target
+ // edi : constructor function
+ Label slow, non_function_call;
+
+ // Check that function is not a smi.
+ __ JumpIfSmi(edi, &non_function_call);
+ // Check that function is a JSFunction.
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &slow);
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Jump to the function-specific construct stub.
+ __ mov(ebx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(ebx, FieldOperand(ebx, SharedFunctionInfo::kConstructStubOffset));
+ __ lea(ebx, FieldOperand(ebx, Code::kHeaderSize));
+ __ jmp(ebx);
+
+ // edi: called object
+ // eax: number of arguments
+ // ecx: object map
+ Label do_call;
+ __ bind(&slow);
+ __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function_call);
+ __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
+ __ jmp(&do_call);
+
+ __ bind(&non_function_call);
+ __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ bind(&do_call);
+ // Set expected number of arguments to zero (not changing eax).
+ __ Set(ebx, Immediate(0));
+ Handle<Code> arguments_adaptor =
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ SetCallKind(ecx, CALL_AS_METHOD);
+ __ jmp(arguments_adaptor, RelocInfo::CODE_TARGET);
+}
bool CEntryStub::NeedsImmovableCode() {
=======================================
--- /branches/bleeding_edge/src/ia32/debug-ia32.cc Fri Dec 2 02:01:50 2011
+++ /branches/bleeding_edge/src/ia32/debug-ia32.cc Fri Jan 27 05:03:19 2012
@@ -228,10 +228,11 @@
// above IC call.
// ----------- S t a t e -------------
// -- eax: number of arguments (not smi)
+ // -- ebx: cache cell for call target
// -- edi: constructor function
// -----------------------------------
// The number of arguments in eax is not smi encoded.
- Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
+ Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), eax.bit(),
false);
}
@@ -245,11 +246,12 @@
void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
- // Register state for stub CallFunction (from CallFunctionStub in
ic-ia32.cc).
+ // Register state for CallFunctionStub (from code-stubs-ia32.cc).
// ----------- S t a t e -------------
+ // -- ebx: cache cell for call target
// -- edi: function
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, edi.bit(), 0, false);
+ Generate_DebugBreakCallHelper(masm, ebx.bit() | edi.bit(), 0, false);
}
=======================================
--- /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Mon Jan 16
07:11:56 2012
+++ /branches/bleeding_edge/src/ia32/full-codegen-ia32.cc Fri Jan 27
05:03:19 2012
@@ -2130,27 +2130,19 @@
SetSourcePosition(expr->position());
// Record call targets in unoptimized code, but not in the snapshot.
- bool record_call_target = !Serializer::enabled();
- if (record_call_target) {
+ if (!Serializer::enabled()) {
flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
- }
- CallFunctionStub stub(arg_count, flags);
- __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
- __ CallStub(&stub, expr->id());
- 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());
+ TypeFeedbackCells::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);
- }
+ RecordTypeFeedbackCell(expr->id(), cell);
+ __ mov(ebx, cell);
+ }
+
+ CallFunctionStub stub(arg_count, flags);
+ __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
@@ -2325,9 +2317,22 @@
__ SafeSet(eax, Immediate(arg_count));
__ mov(edi, Operand(esp, arg_count * kPointerSize));
- Handle<Code> construct_builtin =
- isolate()->builtins()->JSConstructCall();
- __ call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
+ // Record call targets in unoptimized code, but not in the snapshot.
+ CallFunctionFlags flags;
+ if (!Serializer::enabled()) {
+ flags = RECORD_CALL_TARGET;
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<JSGlobalPropertyCell> cell =
+ isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
+ RecordTypeFeedbackCell(expr->id(), cell);
+ __ mov(ebx, cell);
+ } else {
+ flags = NO_CALL_FUNCTION_FLAGS;
+ }
+
+ CallConstructStub stub(flags);
+ __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
context()->Plug(eax);
}
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Tue Jan 24
00:43:12 2012
+++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Fri Jan 27
05:03:19 2012
@@ -3219,9 +3219,9 @@
ASSERT(ToRegister(instr->constructor()).is(edi));
ASSERT(ToRegister(instr->result()).is(eax));
- Handle<Code> builtin = isolate()->builtins()->JSConstructCall();
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
__ Set(eax, Immediate(instr->arity()));
- CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr);
+ CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr);
}
=======================================
--- /branches/bleeding_edge/src/mark-compact.cc Fri Jan 27 01:34:05 2012
+++ /branches/bleeding_edge/src/mark-compact.cc Fri Jan 27 05:03:19 2012
@@ -894,17 +894,9 @@
heap->mark_compact_collector()->flush_monomorphic_ics_)) {
IC::Clear(rinfo->pc());
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);
-
heap->mark_compact_collector()->RecordRelocSlot(rinfo, target);
}
@@ -1025,8 +1017,17 @@
}
static void VisitCode(Map* map, HeapObject* object) {
- reinterpret_cast<Code*>(object)->CodeIterateBody<StaticMarkingVisitor>(
- map->GetHeap());
+ Heap* heap = map->GetHeap();
+ Code* code = reinterpret_cast<Code*>(object);
+ if (FLAG_cleanup_code_caches_at_gc) {
+ TypeFeedbackCells* type_feedback_cells = code->type_feedback_cells();
+ for (int i = 0; i < type_feedback_cells->CellCount(); i++) {
+ ASSERT(type_feedback_cells->AstId(i)->IsSmi());
+ JSGlobalPropertyCell* cell = type_feedback_cells->Cell(i);
+ cell->set_value(TypeFeedbackCells::RawUninitializedSentinel(heap));
+ }
+ }
+ code->CodeIterateBody<StaticMarkingVisitor>(heap);
}
// Code flushing support.
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Thu Jan 26 13:47:57 2012
+++ /branches/bleeding_edge/src/objects-inl.h Fri Jan 27 05:03:19 2012
@@ -45,7 +45,7 @@
#include "spaces.h"
#include "store-buffer.h"
#include "v8memory.h"
-
+#include "factory.h"
#include "incremental-marking.h"
namespace v8 {
@@ -552,6 +552,16 @@
if (FixedArray::cast(this)->length() % 2 != 0) return false;
return true;
}
+
+
+bool Object::IsTypeFeedbackCells() {
+ if (!IsFixedArray()) return false;
+ // There's actually no way to see the difference between a fixed array
and
+ // a cache cells array. Since this is used for asserts we can check that
+ // the length is plausible though.
+ if (FixedArray::cast(this)->length() % 2 != 0) return false;
+ return true;
+}
bool Object::IsContext() {
@@ -2125,6 +2135,7 @@
CAST_ACCESSOR(DescriptorArray)
CAST_ACCESSOR(DeoptimizationInputData)
CAST_ACCESSOR(DeoptimizationOutputData)
+CAST_ACCESSOR(TypeFeedbackCells)
CAST_ACCESSOR(SymbolTable)
CAST_ACCESSOR(JSFunctionResultCache)
CAST_ACCESSOR(NormalizedMapCache)
@@ -3189,18 +3200,6 @@
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() {
@@ -4096,6 +4095,8 @@
ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
ACCESSORS(Code, handler_table, FixedArray, kHandlerTableOffset)
ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
+ACCESSORS(Code, type_feedback_cells, TypeFeedbackCells,
+ kTypeFeedbackCellsOffset)
ACCESSORS(Code, gc_metadata, Object, kGCMetadataOffset)
@@ -4734,6 +4735,41 @@
if (length() == 0) return this;
return GetHeap()->CopyFixedDoubleArray(this);
}
+
+
+void TypeFeedbackCells::SetAstId(int index, Smi* id) {
+ set(1 + index * 2, id);
+}
+
+
+Smi* TypeFeedbackCells::AstId(int index) {
+ return Smi::cast(get(1 + index * 2));
+}
+
+
+void TypeFeedbackCells::SetCell(int index, JSGlobalPropertyCell* cell) {
+ set(index * 2, cell);
+}
+
+
+JSGlobalPropertyCell* TypeFeedbackCells::Cell(int index) {
+ return JSGlobalPropertyCell::cast(get(index * 2));
+}
+
+
+Handle<Object> TypeFeedbackCells::UninitializedSentinel(Isolate* isolate) {
+ return isolate->factory()->the_hole_value();
+}
+
+
+Handle<Object> TypeFeedbackCells::MegamorphicSentinel(Isolate* isolate) {
+ return isolate->factory()->undefined_value();
+}
+
+
+Object* TypeFeedbackCells::RawUninitializedSentinel(Heap* heap) {
+ return heap->raw_unchecked_the_hole_value();
+}
Relocatable::Relocatable(Isolate* isolate) {
=======================================
--- /branches/bleeding_edge/src/objects-visiting-inl.h Fri Nov 11 05:48:14
2011
+++ /branches/bleeding_edge/src/objects-visiting-inl.h Fri Jan 27 05:03:19
2012
@@ -109,6 +109,7 @@
IteratePointer(v, kRelocationInfoOffset);
IteratePointer(v, kHandlerTableOffset);
IteratePointer(v, kDeoptimizationDataOffset);
+ IteratePointer(v, kTypeFeedbackCellsOffset);
RelocIterator it(this, mode_mask);
for (; !it.done(); it.next()) {
@@ -138,6 +139,9 @@
StaticVisitor::VisitPointer(
heap,
reinterpret_cast<Object**>(this->address() +
kDeoptimizationDataOffset));
+ StaticVisitor::VisitPointer(
+ heap,
+ reinterpret_cast<Object**>(this->address() +
kTypeFeedbackCellsOffset));
RelocIterator it(this, mode_mask);
for (; !it.done(); it.next()) {
=======================================
--- /branches/bleeding_edge/src/objects.h Thu Jan 26 13:47:57 2012
+++ /branches/bleeding_edge/src/objects.h Fri Jan 27 05:03:19 2012
@@ -793,6 +793,7 @@
V(DescriptorArray) \
V(DeoptimizationInputData) \
V(DeoptimizationOutputData) \
+ V(TypeFeedbackCells) \
V(FixedArray) \
V(FixedDoubleArray) \
V(Context) \
@@ -3980,8 +3981,44 @@
};
-class SafepointEntry;
-
+// Forward declaration.
+class JSGlobalPropertyCell;
+
+// TypeFeedbackCells is a fixed array used to hold the association between
+// cache cells and AST ids for code generated by the full compiler.
+// The format of the these objects is
+// [i * 2]: Global property cell of ith cache cell.
+// [i * 2 + 1]: Ast ID for ith cache cell.
+class TypeFeedbackCells: public FixedArray {
+ public:
+ int CellCount() { return length() / 2; }
+ static int LengthOfFixedArray(int cell_count) { return cell_count * 2; }
+
+ // Accessors for AST ids associated with cache values.
+ inline Smi* AstId(int index);
+ inline void SetAstId(int index, Smi* id);
+
+ // Accessors for global property cells holding the cache values.
+ inline JSGlobalPropertyCell* Cell(int index);
+ inline void SetCell(int index, JSGlobalPropertyCell* cell);
+
+ // The object that indicates an uninitialized cache.
+ static inline Handle<Object> UninitializedSentinel(Isolate* isolate);
+
+ // The object that indicates a megamorphic state.
+ static inline Handle<Object> MegamorphicSentinel(Isolate* isolate);
+
+ // A raw version of the uninitialized sentinel that's safe to read during
+ // garbage collection (e.g., for patching the cache).
+ static inline Object* RawUninitializedSentinel(Heap* heap);
+
+ // Casting.
+ static inline TypeFeedbackCells* cast(Object* obj);
+};
+
+
+// Forward declaration.
+class SafepointEntry;
// Code describes objects with on-the-fly generated machine code.
class Code: public HeapObject {
@@ -4053,6 +4090,9 @@
// [deoptimization_data]: Array containing data for deopt.
DECL_ACCESSORS(deoptimization_data, FixedArray)
+ // [type_feedback_cells]: Array containing cache cells used for type
feedback.
+ DECL_ACCESSORS(type_feedback_cells, TypeFeedbackCells)
+
// [gc_metadata]: Field used to hold GC related metadata. The contents
of this
// field does not have to be traced during garbage collection since
// it is only used by the garbage collector itself.
@@ -4161,11 +4201,6 @@
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);
@@ -4280,7 +4315,9 @@
static const int kHandlerTableOffset = kRelocationInfoOffset +
kPointerSize;
static const int kDeoptimizationDataOffset =
kHandlerTableOffset + kPointerSize;
- static const int kGCMetadataOffset = kDeoptimizationDataOffset +
kPointerSize;
+ static const int kTypeFeedbackCellsOffset =
+ kDeoptimizationDataOffset + kPointerSize;
+ static const int kGCMetadataOffset = kTypeFeedbackCellsOffset +
kPointerSize;
static const int kFlagsOffset = kGCMetadataOffset + kPointerSize;
static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
@@ -4304,7 +4341,6 @@
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:
@@ -5701,7 +5737,6 @@
// Forward declaration.
class JSBuiltinsObject;
-class JSGlobalPropertyCell;
// Common super class for JavaScript global objects and the special
// builtins global objects.
=======================================
--- /branches/bleeding_edge/src/type-info.cc Mon Jan 16 01:44:35 2012
+++ /branches/bleeding_edge/src/type-info.cc Fri Jan 27 05:03:19 2012
@@ -138,6 +138,12 @@
Handle<Object> value = GetInfo(expr->id());
return value->IsMap() || value->IsSmi() || value->IsJSFunction();
}
+
+
+bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) {
+ Handle<Object> value = GetInfo(expr->id());
+ return value->IsJSFunction();
+}
Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property*
expr) {
@@ -541,6 +547,7 @@
GetRelocInfos(code, &infos);
CreateDictionary(code, &infos);
ProcessRelocInfos(&infos);
+ ProcessTypeFeedbackCells(code);
// Allocate handle in the parent scope.
dictionary_ = scope.CloseAndEscape(dictionary_);
}
@@ -619,23 +626,25 @@
SetInfo(ast_id, target);
break;
- case Code::STUB:
- if (target->major_key() == CodeStub::CallFunction &&
- target->has_function_cache()) {
- Object* value =
CallFunctionStub::GetCachedValue(reloc_entry.pc());
- if (value->IsJSFunction() &&
- !CanRetainOtherContext(JSFunction::cast(value),
- *global_context_)) {
- SetInfo(ast_id, value);
- }
- }
- break;
-
default:
break;
}
}
}
+
+
+void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) {
+ Handle<TypeFeedbackCells> cache(code->type_feedback_cells());
+ for (int i = 0; i < cache->CellCount(); i++) {
+ unsigned ast_id = cache->AstId(i)->value();
+ Object* value = cache->Cell(i)->value();
+ if (value->IsJSFunction() &&
+ !CanRetainOtherContext(JSFunction::cast(value),
+ *global_context_)) {
+ SetInfo(ast_id, value);
+ }
+ }
+}
void TypeFeedbackOracle::SetInfo(unsigned ast_id, Object* target) {
=======================================
--- /branches/bleeding_edge/src/type-info.h Wed Jan 25 08:31:25 2012
+++ /branches/bleeding_edge/src/type-info.h Fri Jan 27 05:03:19 2012
@@ -219,6 +219,7 @@
class Assignment;
class BinaryOperation;
class Call;
+class CallNew;
class CaseClause;
class CompareOperation;
class CompilationInfo;
@@ -240,6 +241,7 @@
bool StoreIsMonomorphicNormal(Expression* expr);
bool StoreIsMegamorphicWithTypeInfo(Expression* expr);
bool CallIsMonomorphic(Call* expr);
+ bool CallNewIsMonomorphic(CallNew* expr);
Handle<Map> LoadMonomorphicReceiverType(Property* expr);
Handle<Map> StoreMonomorphicReceiverType(Expression* expr);
@@ -297,6 +299,7 @@
byte* old_start,
byte* new_start);
void ProcessRelocInfos(ZoneList<RelocInfo>* infos);
+ void ProcessTypeFeedbackCells(Handle<Code> code);
// Returns an element from the backing store. Returns undefined if
// there is no information.
=======================================
--- /branches/bleeding_edge/src/x64/builtins-x64.cc Thu Jan 26 13:47:57 2012
+++ /branches/bleeding_edge/src/x64/builtins-x64.cc Fri Jan 27 05:03:19 2012
@@ -73,49 +73,14 @@
}
-void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
+static void Generate_JSConstructStubHelper(MacroAssembler* masm,
+ bool is_api_function,
+ bool count_constructions) {
// ----------- S t a t e -------------
// -- rax: number of arguments
// -- rdi: constructor function
// -----------------------------------
- Label slow, non_function_call;
- // Check that function is not a smi.
- __ JumpIfSmi(rdi, &non_function_call);
- // Check that function is a JSFunction.
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(not_equal, &slow);
-
- // Jump to the function-specific construct stub.
- __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
- __ movq(rbx, FieldOperand(rbx,
SharedFunctionInfo::kConstructStubOffset));
- __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize));
- __ jmp(rbx);
-
- // rdi: called object
- // rax: number of arguments
- // rcx: object map
- Label do_call;
- __ bind(&slow);
- __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
- __ j(not_equal, &non_function_call);
- __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
- __ jmp(&do_call);
-
- __ bind(&non_function_call);
- __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
- __ bind(&do_call);
- // Set expected number of arguments to zero (not changing rax).
- __ Set(rbx, 0);
- __ SetCallKind(rcx, CALL_AS_METHOD);
- __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
- RelocInfo::CODE_TARGET);
-}
-
-
-static void Generate_JSConstructStubHelper(MacroAssembler* masm,
- bool is_api_function,
- bool count_constructions) {
// Should never count constructions for api objects.
ASSERT(!is_api_function || !count_constructions);
@@ -515,8 +480,8 @@
// Invoke the code.
if (is_construct) {
// Expects rdi to hold function pointer.
- __ Call(masm->isolate()->builtins()->JSConstructCall(),
- RelocInfo::CODE_TARGET);
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
+ __ CallStub(&stub);
} else {
ParameterCount actual(rax);
// Function must be in rdi.
=======================================
--- /branches/bleeding_edge/src/x64/code-stubs-x64.cc Tue Jan 17 06:29:17
2012
+++ /branches/bleeding_edge/src/x64/code-stubs-x64.cc Fri Jan 27 05:03:19
2012
@@ -3579,24 +3579,47 @@
}
-void CallFunctionStub::FinishCode(Handle<Code> code) {
- code->set_has_function_cache(false);
-}
-
-
-void CallFunctionStub::Clear(Heap* heap, Address address) {
- UNREACHABLE();
-}
-
-
-Object* CallFunctionStub::GetCachedValue(Address address) {
- UNREACHABLE();
- return NULL;
+static void GenerateRecordCallTarget(MacroAssembler* masm) {
+ // Cache the called function in a global property cell. Cache states
+ // are uninitialized, monomorphic (indicated by a JSFunction), and
+ // megamorphic.
+ // rbx : cache cell for call target
+ // rdi : the function to call
+ Isolate* isolate = masm->isolate();
+ Label initialize, done;
+
+ // Load the cache state into rcx.
+ __ movq(rcx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset));
+
+ // A monomorphic cache hit or an already megamorphic state: invoke the
+ // function without changing the state.
+ __ cmpq(rcx, rdi);
+ __ j(equal, &done, Label::kNear);
+ __ Cmp(rcx, TypeFeedbackCells::MegamorphicSentinel(isolate));
+ __ j(equal, &done, Label::kNear);
+
+ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
+ // megamorphic.
+ __ Cmp(rcx, TypeFeedbackCells::UninitializedSentinel(isolate));
+ __ j(equal, &initialize, Label::kNear);
+ // MegamorphicSentinel is an immortal immovable object (undefined) so no
+ // write-barrier is needed.
+ __ Move(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset),
+ TypeFeedbackCells::MegamorphicSentinel(isolate));
+ __ jmp(&done, Label::kNear);
+
+ // An uninitialized cache is patched with the function.
+ __ bind(&initialize);
+ __ movq(FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset), rdi);
+ // No need for a write barrier here - cells are rescanned.
+
+ __ bind(&done);
}
void CallFunctionStub::Generate(MacroAssembler* masm) {
// rdi : the function to call
+ // rbx : cache cell for call target
Label slow, non_function;
// The receiver might implicitly be the global object. This is
@@ -3673,6 +3696,49 @@
Isolate::Current()->builtins()->ArgumentsAdaptorTrampoline();
__ Jump(adaptor, RelocInfo::CODE_TARGET);
}
+
+
+void CallConstructStub::Generate(MacroAssembler* masm) {
+ // rax : number of arguments
+ // rbx : cache cell for call target
+ // rdi : constructor function
+ Label slow, non_function_call;
+
+ // Check that function is not a smi.
+ __ JumpIfSmi(rdi, &non_function_call);
+ // Check that function is a JSFunction.
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &slow);
+
+ if (RecordCallTarget()) {
+ GenerateRecordCallTarget(masm);
+ }
+
+ // Jump to the function-specific construct stub.
+ __ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movq(rbx, FieldOperand(rbx,
SharedFunctionInfo::kConstructStubOffset));
+ __ lea(rbx, FieldOperand(rbx, Code::kHeaderSize));
+ __ jmp(rbx);
+
+ // rdi: called object
+ // rax: number of arguments
+ // rcx: object map
+ Label do_call;
+ __ bind(&slow);
+ __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function_call);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
+ __ jmp(&do_call);
+
+ __ bind(&non_function_call);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
+ __ bind(&do_call);
+ // Set expected number of arguments to zero (not changing rax).
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+}
bool CEntryStub::NeedsImmovableCode() {
=======================================
--- /branches/bleeding_edge/src/x64/debug-x64.cc Mon Dec 5 00:58:01 2011
+++ /branches/bleeding_edge/src/x64/debug-x64.cc Fri Jan 27 05:03:19 2012
@@ -235,9 +235,10 @@
// above IC call.
// ----------- S t a t e -------------
// -- rax: number of arguments
+ // -- rbx: cache cell for call target
// -----------------------------------
// The number of arguments in rax is not smi encoded.
- Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
+ Generate_DebugBreakCallHelper(masm, rbx.bit() | rdi.bit(), rax.bit(),
false);
}
@@ -251,7 +252,7 @@
void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
- // Register state for stub CallFunction (from CallFunctionStub in
ic-x64.cc).
+ // Register state for CallFunctionStub (from code-stubs-x64.cc).
// ----------- S t a t e -------------
// -- rdi : function
// -----------------------------------
=======================================
--- /branches/bleeding_edge/src/x64/full-codegen-x64.cc Mon Jan 16 07:11:56
2012
+++ /branches/bleeding_edge/src/x64/full-codegen-x64.cc Fri Jan 27 05:03:19
2012
@@ -2277,9 +2277,22 @@
__ Set(rax, arg_count);
__ movq(rdi, Operand(rsp, arg_count * kPointerSize));
- Handle<Code> construct_builtin =
- isolate()->builtins()->JSConstructCall();
- __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
+ // Record call targets in unoptimized code, but not in the snapshot.
+ CallFunctionFlags flags;
+ if (!Serializer::enabled()) {
+ flags = RECORD_CALL_TARGET;
+ Handle<Object> uninitialized =
+ TypeFeedbackCells::UninitializedSentinel(isolate());
+ Handle<JSGlobalPropertyCell> cell =
+ isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
+ RecordTypeFeedbackCell(expr->id(), cell);
+ __ Move(rbx, cell);
+ } else {
+ flags = NO_CALL_FUNCTION_FLAGS;
+ }
+
+ CallConstructStub stub(flags);
+ __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
context()->Plug(rax);
}
=======================================
--- /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Tue Jan 24
00:43:12 2012
+++ /branches/bleeding_edge/src/x64/lithium-codegen-x64.cc Fri Jan 27
05:03:19 2012
@@ -3123,9 +3123,9 @@
ASSERT(ToRegister(instr->InputAt(0)).is(rdi));
ASSERT(ToRegister(instr->result()).is(rax));
- Handle<Code> builtin = isolate()->builtins()->JSConstructCall();
+ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
__ Set(rax, instr->arity());
- CallCode(builtin, RelocInfo::CONSTRUCT_CALL, instr);
+ CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr);
}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev