From 9e5757abeeef2b93b40d22dfc6635dd4cd3480aa Mon Sep 17 00:00:00 2001 From: "mvstanton@chromium.org" Date: Fri, 4 Apr 2014 16:18:59 +0000 Subject: [PATCH] Revert "Reland of https://codereview.chromium.org/172523002/" This reverts commit r20516 due to a Sunspider performance issue. R=ishell@chromium.org Review URL: https://codereview.chromium.org/226233002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20521 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/builtins-arm.cc | 2 +- src/arm/code-stubs-arm.cc | 188 ++++++-------------------- src/arm/debug-arm.cc | 31 +++-- src/arm/full-codegen-arm.cc | 87 ++++++++---- src/arm/lithium-codegen-arm.cc | 2 +- src/arm64/builtins-arm64.cc | 2 +- src/arm64/code-stubs-arm64.cc | 207 +++++++---------------------- src/arm64/debug-arm64.cc | 31 +++-- src/arm64/full-codegen-arm64.cc | 87 ++++++++---- src/arm64/lithium-codegen-arm64.cc | 2 +- src/ast.cc | 7 +- src/builtins.cc | 11 +- src/builtins.h | 4 +- src/code-stubs.cc | 6 +- src/code-stubs.h | 72 +++------- src/debug.cc | 29 ++-- src/debug.h | 5 +- src/full-codegen.h | 14 +- src/ia32/builtins-ia32.cc | 2 +- src/ia32/code-stubs-ia32.cc | 189 ++++++-------------------- src/ia32/debug-ia32.cc | 33 +++-- src/ia32/full-codegen-ia32.cc | 105 ++++++++++----- src/ia32/lithium-codegen-ia32.cc | 2 +- src/ic.cc | 168 +---------------------- src/ic.h | 127 ------------------ src/log.cc | 4 - src/log.h | 1 - src/objects-inl.h | 4 +- src/objects-visiting-inl.h | 7 - src/objects.cc | 3 - src/objects.h | 2 - src/v8globals.h | 9 +- src/x64/builtins-x64.cc | 2 +- src/x64/code-stubs-x64.cc | 193 ++++++--------------------- src/x64/debug-x64.cc | 33 +++-- src/x64/full-codegen-x64.cc | 90 +++++++++---- src/x64/lithium-codegen-x64.cc | 2 +- 37 files changed, 570 insertions(+), 1193 deletions(-) diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 5549bff88c..f138146417 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -807,7 +807,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, if (is_construct) { // No type feedback cell is available __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ CallStub(&stub); } else { ParameterCount actual(r0); diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 6e6c9eb841..81691a347f 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -2977,17 +2977,14 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) { } -static void GenericCallHelper(MacroAssembler* masm, - const CallIC::State& state, - bool wrap_and_call = false) { +void CallFunctionStub::Generate(MacroAssembler* masm) { // r1 : the function to call - - // wrap_and_call can only be true if we are compiling a monomorphic method. - ASSERT(!(wrap_and_call && state.IsGeneric())); - ASSERT(!wrap_and_call || state.CallAsMethod()); + // r2 : feedback vector + // r3 : (only if r2 is not the megamorphic symbol) slot in feedback + // vector (Smi) Label slow, non_function, wrap, cont; - if (state.IsGeneric()) { + if (NeedsChecks()) { // Check that the function is really a JavaScript function. // r1: pushed function (to be verified) __ JumpIfSmi(r1, &non_function); @@ -2995,15 +2992,22 @@ static void GenericCallHelper(MacroAssembler* masm, // Goto slow case if we do not have a function. __ CompareObjectType(r1, r4, r4, JS_FUNCTION_TYPE); __ b(ne, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + // Type information was updated. Because we may call Array, which + // expects either undefined or an AllocationSite in ebx we need + // to set ebx to undefined. + __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); + } } // Fast-case: Invoke the function now. // r1: pushed function - int argc = state.arg_count(); - ParameterCount actual(argc); + ParameterCount actual(argc_); - if (state.CallAsMethod()) { - if (state.IsGeneric()) { + if (CallAsMethod()) { + if (NeedsChecks()) { // Do not transform the receiver for strict mode functions. __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); __ ldr(r4, FieldMemOperand(r3, SharedFunctionInfo::kCompilerHintsOffset)); @@ -3016,36 +3020,39 @@ static void GenericCallHelper(MacroAssembler* masm, __ b(ne, &cont); } - if (state.IsGeneric() || state.IsSloppy() || wrap_and_call) { - // Compute the receiver in sloppy mode. - __ ldr(r3, MemOperand(sp, argc * kPointerSize)); + // Compute the receiver in sloppy mode. + __ ldr(r3, MemOperand(sp, argc_ * kPointerSize)); - if (state.IsGeneric()) { - __ JumpIfSmi(r3, &wrap); - __ CompareObjectType(r3, r4, r4, FIRST_SPEC_OBJECT_TYPE); - __ b(lt, &wrap); - } else { - __ jmp(&wrap); - } + if (NeedsChecks()) { + __ JumpIfSmi(r3, &wrap); + __ CompareObjectType(r3, r4, r4, FIRST_SPEC_OBJECT_TYPE); + __ b(lt, &wrap); + } else { + __ jmp(&wrap); } __ bind(&cont); } + __ InvokeFunction(r1, actual, JUMP_FUNCTION, NullCallWrapper()); - if (state.ArgumentsMustMatch()) { - __ InvokeFunction(r1, actual, actual, JUMP_FUNCTION, NullCallWrapper()); - } else { - __ InvokeFunction(r1, actual, JUMP_FUNCTION, NullCallWrapper()); - } - - if (state.IsGeneric()) { + if (NeedsChecks()) { // Slow-case: Non-function called. __ bind(&slow); + if (RecordCallTarget()) { + // If there is a call target cache, mark it megamorphic in the + // non-function case. MegamorphicSentinel is an immortal immovable + // object (megamorphic symbol) so no write barrier is needed. + ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), + masm->isolate()->heap()->megamorphic_symbol()); + __ add(r5, r2, Operand::PointerOffsetFromSmiKey(r3)); + __ LoadRoot(ip, Heap::kMegamorphicSymbolRootIndex); + __ str(ip, FieldMemOperand(r5, FixedArray::kHeaderSize)); + } // Check for function proxy. __ cmp(r4, Operand(JS_FUNCTION_PROXY_TYPE)); __ b(ne, &non_function); __ push(r1); // put proxy as additional argument - __ mov(r0, Operand(argc + 1, RelocInfo::NONE32)); + __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE32)); __ mov(r2, Operand::Zero()); __ GetBuiltinFunction(r1, Builtins::CALL_FUNCTION_PROXY); { @@ -3057,62 +3064,28 @@ static void GenericCallHelper(MacroAssembler* masm, // CALL_NON_FUNCTION expects the non-function callee as receiver (instead // of the original receiver from the call site). __ bind(&non_function); - __ str(r1, MemOperand(sp, argc * kPointerSize)); - __ mov(r0, Operand(argc)); // Set up the number of arguments. + __ str(r1, MemOperand(sp, argc_ * kPointerSize)); + __ mov(r0, Operand(argc_)); // Set up the number of arguments. __ mov(r2, Operand::Zero()); __ GetBuiltinFunction(r1, Builtins::CALL_NON_FUNCTION); __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), RelocInfo::CODE_TARGET); } - if (state.CallAsMethod()) { + if (CallAsMethod()) { __ bind(&wrap); - - if (!state.IsGeneric() && !wrap_and_call) { - __ ldr(r5, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r4, FieldMemOperand(r5, SharedFunctionInfo::kCompilerHintsOffset)); - - // Do not transform the receiver for native - __ tst(r4, Operand(1 << (SharedFunctionInfo::kNative + kSmiTagSize))); - __ b(ne, &cont); - } - // Wrap the receiver and patch it back onto the stack. { FrameAndConstantPoolScope frame_scope(masm, StackFrame::INTERNAL); __ Push(r1, r3); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ pop(r1); } - __ str(r0, MemOperand(sp, argc * kPointerSize)); + __ str(r0, MemOperand(sp, argc_ * kPointerSize)); __ jmp(&cont); } } -void CallFunctionStub::Generate(MacroAssembler* masm) { - // r1 : the function to call - - // GenericCallHelper expresses it's options in terms of CallIC::State. - CallIC::CallType call_type = CallAsMethod() ? - CallIC::METHOD : CallIC::FUNCTION; - - if (NeedsChecks()) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - argc_, - call_type)); - } else { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - argc_, - call_type, - CallIC::ARGUMENTS_COUNT_UNKNOWN, - SLOPPY), - true); - } -} - - void CallConstructStub::Generate(MacroAssembler* masm) { // r0 : number of arguments // r1 : the function to call @@ -3177,85 +3150,6 @@ void CallConstructStub::Generate(MacroAssembler* masm) { } -void CallICStub::GenerateMonomorphicCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - state_.arg_count(), - state_.call_type(), - state_.argument_check(), - state_.strict_mode())); -} - - -void CallICStub::GenerateSlowCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - state_.arg_count(), - state_.call_type())); -} - - -void CallICStub::Generate(MacroAssembler* masm) { - // r1 - function - // r2 - vector - // r3 - slot id (Smi) - Label extra_checks_or_miss, slow; - - // The checks. First, does r1 match the recorded monomorphic target? - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ ldr(r4, FieldMemOperand(r4, FixedArray::kHeaderSize)); - __ cmp(r1, r4); - __ b(ne, &extra_checks_or_miss); - - GenerateMonomorphicCall(masm); - - __ bind(&extra_checks_or_miss); - if (IsGeneric()) { - Label miss_uninit; - - __ CompareRoot(r4, Heap::kMegamorphicSymbolRootIndex); - __ b(eq, &slow); - __ CompareRoot(r4, Heap::kUninitializedSymbolRootIndex); - __ b(eq, &miss_uninit); - // If we get here, go from monomorphic to megamorphic, Don't bother missing, - // just update. - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ LoadRoot(ip, Heap::kMegamorphicSymbolRootIndex); - __ str(ip, FieldMemOperand(r4, FixedArray::kHeaderSize)); - __ jmp(&slow); - - __ bind(&miss_uninit); - } - - GenerateMiss(masm); - - // the slow case - __ bind(&slow); - GenerateSlowCall(masm); -} - - -void CallICStub::GenerateMiss(MacroAssembler* masm) { - // Get the receiver of the function from the stack; 1 ~ return address. - __ ldr(r4, MemOperand(sp, (state_.arg_count() + 1) * kPointerSize)); - - { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - - // Push the receiver and the function and feedback info. - __ Push(r4, r1, r2, r3); - - // Call the entry. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCallIC_Miss), - masm->isolate()); - __ CallExternalReference(miss, 4); - - // Move result to edi and exit the internal frame. - __ mov(r1, r0); - } -} - - // StringCharCodeAtGenerator void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { Label flat_string; diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc index d3850484e6..12258ccad9 100644 --- a/src/arm/debug-arm.cc +++ b/src/arm/debug-arm.cc @@ -179,17 +179,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, } -void Debug::GenerateCallICStubDebugBreak(MacroAssembler* masm) { - // Register state for CallICStub - // ----------- S t a t e ------------- - // -- r1 : function - // -- r2 : feedback array - // -- r3 : slot in feedback array (smi) - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), 0); -} - - void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Calling convention for IC load (from ic-arm.cc). // ----------- S t a t e ------------- @@ -246,6 +235,15 @@ void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { + // Calling convention for IC call (from ic-arm.cc) + // ----------- S t a t e ------------- + // -- r2 : name + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, r2.bit(), 0); +} + + void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that r0 is TOS which // is an object - this is not generally the case so this should be used with @@ -263,6 +261,17 @@ void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-arm.cc). + // ----------- S t a t e ------------- + // -- r1 : function + // -- r2 : feedback array + // -- r3 : slot in feedback array + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, r1.bit() | r2.bit() | r3.bit(), 0); +} + + void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { // Calling convention for CallConstructStub (from code-stubs-arm.cc) // ----------- S t a t e ------------- diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 8735f86f6b..4ea2942f4d 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -2632,15 +2632,14 @@ void FullCodeGenerator::CallIC(Handle code, // Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { +void FullCodeGenerator::EmitCallWithIC(Call* expr) { Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); - CallIC::CallType call_type = callee->IsVariableProxy() - ? CallIC::FUNCTION - : CallIC::METHOD; - + CallFunctionFlags flags; // Get the target function. - if (call_type == CallIC::FUNCTION) { + if (callee->IsVariableProxy()) { { StackValueContext context(this); EmitVariableLoad(callee->AsVariableProxy()); PrepareForBailout(callee, NO_REGISTERS); @@ -2648,6 +2647,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push undefined as receiver. This is patched in the method prologue if it // is a sloppy mode method. __ Push(isolate()->factory()->undefined_value()); + flags = NO_CALL_FUNCTION_FLAGS; } else { // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2658,19 +2658,40 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { __ ldr(ip, MemOperand(sp, 0)); __ push(ip); __ str(r0, MemOperand(sp, kPointerSize)); + flags = CALL_AS_METHOD; } - EmitCall(expr, call_type); + // Load the arguments. + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, flags); + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + + // Restore context register. + __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, r0); } // Code common for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, - Expression* key) { +void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, + Expression* key) { // Load the key. VisitForAccumulatorValue(key); Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2683,12 +2704,28 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, __ push(ip); __ str(r0, MemOperand(sp, kPointerSize)); - EmitCall(expr, CallIC::METHOD); + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, CALL_AS_METHOD); + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + // Restore context register. + __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, r0); } -void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { - // Load the arguments. +void FullCodeGenerator::EmitCallWithStub(Call* expr) { + // Code common for calls using the call stub. ZoneList* args = expr->arguments(); int arg_count = args->length(); { PreservePositionScope scope(masm()->positions_recorder()); @@ -2696,21 +2733,19 @@ void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { VisitForStackValue(args->at(i)); } } - - // Record source position of the IC call. + // Record source position for debugger. SetSourcePosition(expr->position()); - Handle ic = CallIC::initialize_stub( - isolate(), arg_count, call_type); + Handle uninitialized = TypeFeedbackInfo::UninitializedSentinel(isolate()); StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); __ Move(r2, FeedbackVector()); __ mov(r3, Operand(Smi::FromInt(expr->CallFeedbackSlot()))); - __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); - // Don't assign a type feedback id to the IC, since type feedback is provided - // by the vector above. - CallIC(ic); + // Record call targets in unoptimized code. + CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2793,7 +2828,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); context()->DropAndPlug(1, r0); } else if (call_type == Call::GLOBAL_CALL) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else if (call_type == Call::LOOKUP_SLOT_CALL) { // Call to a lookup slot (dynamically introduced variable). @@ -2833,16 +2868,16 @@ void FullCodeGenerator::VisitCall(Call* expr) { // The receiver is either the global receiver or an object found // by LoadContextSlot. - EmitCall(expr); + EmitCallWithStub(expr); } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); { PreservePositionScope scope(masm()->positions_recorder()); VisitForStackValue(property->obj()); } if (property->key()->IsPropertyName()) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else { - EmitKeyedCallWithLoadIC(expr, property->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { ASSERT(call_type == Call::OTHER_CALL); @@ -2853,7 +2888,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ LoadRoot(r1, Heap::kUndefinedValueRootIndex); __ push(r1); // Emit function call. - EmitCall(expr); + EmitCallWithStub(expr); } #ifdef DEBUG @@ -2903,7 +2938,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ Move(r2, FeedbackVector()); __ mov(r3, Operand(Smi::FromInt(expr->CallNewFeedbackSlot()))); - CallConstructStub stub(RECORD_CONSTRUCTOR_TARGET); + CallConstructStub stub(RECORD_CALL_TARGET); __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(r0); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 55fcfb3436..47b3a1063a 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -3995,7 +3995,7 @@ void LCodeGen::DoCallNew(LCallNew* instr) { __ mov(r0, Operand(instr->arity())); // No cell in r2 for construct type feedback in optimized code __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); } diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 8258be32cd..01ac4cc5db 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -785,7 +785,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // No type feedback cell is available. __ LoadRoot(x2, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ CallStub(&stub); } else { ParameterCount actual(x0); diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index 5c1b7ac2ca..d112271744 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc @@ -3293,36 +3293,42 @@ static void GenerateRecordCallTarget(MacroAssembler* masm, } -static void GenericCallHelper(MacroAssembler* masm, - const CallIC::State& state, - bool wrap_and_call = false) { +void CallFunctionStub::Generate(MacroAssembler* masm) { + ASM_LOCATION("CallFunctionStub::Generate"); // x1 function the function to call - - // wrap_and_call can only be true if we are compiling a monomorphic method. - ASSERT(!(wrap_and_call && state.IsGeneric())); - ASSERT(!wrap_and_call || state.CallAsMethod()); + // x2 : feedback vector + // x3 : slot in feedback vector (smi) (if x2 is not the megamorphic symbol) Register function = x1; + Register cache_cell = x2; + Register slot = x3; Register type = x4; Label slow, non_function, wrap, cont; // TODO(jbramley): This function has a lot of unnamed registers. Name them, // and tidy things up a bit. - if (state.IsGeneric()) { + if (NeedsChecks()) { // Check that the function is really a JavaScript function. __ JumpIfSmi(function, &non_function); // Goto slow case if we do not have a function. __ JumpIfNotObjectType(function, x10, type, JS_FUNCTION_TYPE, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm, x0, function, cache_cell, slot, x4, x5); + // Type information was updated. Because we may call Array, which + // expects either undefined or an AllocationSite in ebx we need + // to set ebx to undefined. + __ LoadRoot(cache_cell, Heap::kUndefinedValueRootIndex); + } } // Fast-case: Invoke the function now. // x1 function pushed function - int argc = state.arg_count(); - ParameterCount actual(argc); + ParameterCount actual(argc_); - if (state.CallAsMethod()) { - if (state.IsGeneric()) { + if (CallAsMethod()) { + if (NeedsChecks()) { // Do not transform the receiver for strict mode functions. __ Ldr(x3, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); __ Ldr(w4, FieldMemOperand(x3, SharedFunctionInfo::kCompilerHintsOffset)); @@ -3332,42 +3338,42 @@ static void GenericCallHelper(MacroAssembler* masm, __ Tbnz(w4, SharedFunctionInfo::kNative, &cont); } - if (state.IsGeneric() || state.IsSloppy() || wrap_and_call) { - // Compute the receiver in sloppy mode. - __ Peek(x3, argc * kPointerSize); + // Compute the receiver in sloppy mode. + __ Peek(x3, argc_ * kPointerSize); - if (state.IsGeneric()) { - __ JumpIfSmi(x3, &wrap); - __ JumpIfObjectType(x3, x10, type, FIRST_SPEC_OBJECT_TYPE, &wrap, lt); - } else { - __ B(&wrap); - } + if (NeedsChecks()) { + __ JumpIfSmi(x3, &wrap); + __ JumpIfObjectType(x3, x10, type, FIRST_SPEC_OBJECT_TYPE, &wrap, lt); + } else { + __ B(&wrap); } __ Bind(&cont); } + __ InvokeFunction(function, + actual, + JUMP_FUNCTION, + NullCallWrapper()); - if (state.ArgumentsMustMatch()) { - __ InvokeFunction(function, - actual, - actual, - JUMP_FUNCTION, - NullCallWrapper()); - } else { - __ InvokeFunction(function, - actual, - JUMP_FUNCTION, - NullCallWrapper()); - } - - if (state.IsGeneric()) { + if (NeedsChecks()) { // Slow-case: Non-function called. __ Bind(&slow); + if (RecordCallTarget()) { + // If there is a call target cache, mark it megamorphic in the + // non-function case. MegamorphicSentinel is an immortal immovable object + // (megamorphic symbol) so no write barrier is needed. + ASSERT_EQ(*TypeFeedbackInfo::MegamorphicSentinel(masm->isolate()), + masm->isolate()->heap()->megamorphic_symbol()); + __ Add(x12, cache_cell, Operand::UntagSmiAndScale(slot, + kPointerSizeLog2)); + __ LoadRoot(x11, Heap::kMegamorphicSymbolRootIndex); + __ Str(x11, FieldMemOperand(x12, FixedArray::kHeaderSize)); + } // Check for function proxy. // x10 : function type. __ CompareAndBranch(type, JS_FUNCTION_PROXY_TYPE, ne, &non_function); __ Push(function); // put proxy as additional argument - __ Mov(x0, argc + 1); + __ Mov(x0, argc_ + 1); __ Mov(x2, 0); __ GetBuiltinFunction(x1, Builtins::CALL_FUNCTION_PROXY); { @@ -3379,62 +3385,28 @@ static void GenericCallHelper(MacroAssembler* masm, // CALL_NON_FUNCTION expects the non-function callee as receiver (instead // of the original receiver from the call site). __ Bind(&non_function); - __ Poke(function, argc * kXRegSize); - __ Mov(x0, argc); // Set up the number of arguments. + __ Poke(function, argc_ * kXRegSize); + __ Mov(x0, argc_); // Set up the number of arguments. __ Mov(x2, 0); __ GetBuiltinFunction(function, Builtins::CALL_NON_FUNCTION); __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(), RelocInfo::CODE_TARGET); } - if (state.CallAsMethod()) { + if (CallAsMethod()) { __ Bind(&wrap); - - if (!state.IsGeneric() && !wrap_and_call) { - __ Ldr(x5, FieldMemOperand(x1, JSFunction::kSharedFunctionInfoOffset)); - __ Ldr(w4, FieldMemOperand(x5, SharedFunctionInfo::kCompilerHintsOffset)); - - // Do not transform the receiver for native - __ Tbnz(w4, SharedFunctionInfo::kNative, &cont); - } - // Wrap the receiver and patch it back onto the stack. { FrameScope frame_scope(masm, StackFrame::INTERNAL); __ Push(x1, x3); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ Pop(x1); } - __ Poke(x0, argc * kPointerSize); + __ Poke(x0, argc_ * kPointerSize); __ B(&cont); } } -void CallFunctionStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("CallFunctionStub::Generate"); - // x1 function the function to call - - // GenericCallHelper expresses it's options in terms of CallIC::State. - CallIC::CallType call_type = CallAsMethod() ? - CallIC::METHOD : CallIC::FUNCTION; - - if (NeedsChecks()) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - argc_, - call_type)); - } else { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - argc_, - call_type, - CallIC::ARGUMENTS_COUNT_UNKNOWN, - SLOPPY), - true); - } -} - - void CallConstructStub::Generate(MacroAssembler* masm) { ASM_LOCATION("CallConstructStub::Generate"); // x0 : number of arguments @@ -3505,93 +3477,6 @@ void CallConstructStub::Generate(MacroAssembler* masm) { } -void CallICStub::GenerateMonomorphicCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - state_.arg_count(), - state_.call_type(), - state_.argument_check(), - state_.strict_mode())); -} - - -void CallICStub::GenerateSlowCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - state_.arg_count(), - state_.call_type())); -} - - -void CallICStub::Generate(MacroAssembler* masm) { - ASM_LOCATION("CallICStub"); - - // x1 - function - // x2 - vector - // x3 - slot id (Smi) - Label extra_checks_or_miss, slow; - Register function = x1; - Register feedback_vector = x2; - Register index = x3; - - // The checks. First, does x1 match the recorded monomorphic target? - __ Add(x4, feedback_vector, - Operand::UntagSmiAndScale(index, kPointerSizeLog2)); - __ Ldr(x4, FieldMemOperand(x4, FixedArray::kHeaderSize)); - - __ Cmp(x4, function); - __ B(ne, &extra_checks_or_miss); - - GenerateMonomorphicCall(masm); - - __ bind(&extra_checks_or_miss); - if (IsGeneric()) { - Label miss_uninit; - - __ JumpIfRoot(x4, Heap::kMegamorphicSymbolRootIndex, &slow); - __ JumpIfRoot(x4, Heap::kUninitializedSymbolRootIndex, &miss_uninit); - // If we get here, go from monomorphic to megamorphic, Don't bother missing, - // just update. - __ Add(x4, feedback_vector, - Operand::UntagSmiAndScale(index, kPointerSizeLog2)); - __ LoadRoot(x5, Heap::kMegamorphicSymbolRootIndex); - __ Str(x5, FieldMemOperand(x4, FixedArray::kHeaderSize)); - __ B(&slow); - - __ bind(&miss_uninit); - } - - GenerateMiss(masm); - - // the slow case - __ bind(&slow); - GenerateSlowCall(masm); -} - - -void CallICStub::GenerateMiss(MacroAssembler* masm) { - ASM_LOCATION("CallICStub[Miss]"); - - // Get the receiver of the function from the stack; 1 ~ return address. - __ Peek(x4, (state_.arg_count() + 1) * kPointerSize); - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Push the receiver and the function and feedback info. - __ Push(x4, x1, x2, x3); - - // Call the entry. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCallIC_Miss), - masm->isolate()); - __ CallExternalReference(miss, 4); - - // Move result to edi and exit the internal frame. - __ Mov(x1, x0); - } -} - - void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // If the receiver is a smi trigger the non-string case. __ JumpIfSmi(object_, receiver_not_string_); diff --git a/src/arm64/debug-arm64.cc b/src/arm64/debug-arm64.cc index 89f0c90065..716337f051 100644 --- a/src/arm64/debug-arm64.cc +++ b/src/arm64/debug-arm64.cc @@ -240,17 +240,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, } -void Debug::GenerateCallICStubDebugBreak(MacroAssembler* masm) { - // Register state for CallICStub - // ----------- S t a t e ------------- - // -- x1 : function - // -- x2 : feedback array - // -- x3 : slot in feedback array - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, x1.Bit() | x2.Bit() | x3.Bit(), 0, x10); -} - - void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Calling convention for IC load (from ic-arm.cc). // ----------- S t a t e ------------- @@ -307,6 +296,15 @@ void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { + // Calling convention for IC call (from ic-arm.cc) + // ----------- S t a t e ------------- + // -- x2 : name + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, x2.Bit(), 0, x10); +} + + void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // In places other than IC call sites it is expected that r0 is TOS which // is an object - this is not generally the case so this should be used with @@ -324,6 +322,17 @@ void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-arm64.cc). + // ----------- S t a t e ------------- + // -- x1 : function + // -- x2 : feedback array + // -- x3 : slot in feedback array + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, x1.Bit() | x2.Bit() | x3.Bit(), 0, x10); +} + + void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { // Calling convention for CallConstructStub (from code-stubs-arm64.cc). // ----------- S t a t e ------------- diff --git a/src/arm64/full-codegen-arm64.cc b/src/arm64/full-codegen-arm64.cc index 5bae367d90..819868b5d8 100644 --- a/src/arm64/full-codegen-arm64.cc +++ b/src/arm64/full-codegen-arm64.cc @@ -2337,15 +2337,16 @@ void FullCodeGenerator::CallIC(Handle code, // Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { +void FullCodeGenerator::EmitCallWithIC(Call* expr) { + ASM_LOCATION("EmitCallWithIC"); + Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); - CallIC::CallType call_type = callee->IsVariableProxy() - ? CallIC::FUNCTION - : CallIC::METHOD; - + CallFunctionFlags flags; // Get the target function. - if (call_type == CallIC::FUNCTION) { + if (callee->IsVariableProxy()) { { StackValueContext context(this); EmitVariableLoad(callee->AsVariableProxy()); PrepareForBailout(callee, NO_REGISTERS); @@ -2353,6 +2354,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push undefined as receiver. This is patched in the method prologue if it // is a sloppy mode method. __ Push(isolate()->factory()->undefined_value()); + flags = NO_CALL_FUNCTION_FLAGS; } else { // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2362,19 +2364,40 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push the target function under the receiver. __ Pop(x10); __ Push(x0, x10); + flags = CALL_AS_METHOD; } - EmitCall(expr, call_type); + // Load the arguments. + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, flags); + __ Peek(x1, (arg_count + 1) * kPointerSize); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + + // Restore context register. + __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, x0); } // Code common for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, - Expression* key) { +void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, + Expression* key) { // Load the key. VisitForAccumulatorValue(key); Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2386,12 +2409,28 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, __ Pop(x10); __ Push(x0, x10); - EmitCall(expr, CallIC::METHOD); + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, CALL_AS_METHOD); + __ Peek(x1, (arg_count + 1) * kPointerSize); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + // Restore context register. + __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, x0); } -void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { - // Load the arguments. +void FullCodeGenerator::EmitCallWithStub(Call* expr) { + // Code common for calls using the call stub. ZoneList* args = expr->arguments(); int arg_count = args->length(); { PreservePositionScope scope(masm()->positions_recorder()); @@ -2399,21 +2438,19 @@ void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { VisitForStackValue(args->at(i)); } } - // Record source position of the IC call. + // Record source position for debugger. SetSourcePosition(expr->position()); - Handle ic = CallIC::initialize_stub( - isolate(), arg_count, call_type); Handle uninitialized = TypeFeedbackInfo::UninitializedSentinel(isolate()); StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); __ LoadObject(x2, FeedbackVector()); __ Mov(x3, Smi::FromInt(expr->CallFeedbackSlot())); - __ Peek(x1, (arg_count + 1) * kXRegSize); - // Don't assign a type feedback id to the IC, since type feedback is provided - // by the vector above. - CallIC(ic); + // Record call targets in unoptimized code. + CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); + __ Peek(x1, (arg_count + 1) * kXRegSize); + __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2505,7 +2542,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, x0); } else if (call_type == Call::GLOBAL_CALL) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else if (call_type == Call::LOOKUP_SLOT_CALL) { // Call to a lookup slot (dynamically introduced variable). @@ -2545,16 +2582,16 @@ void FullCodeGenerator::VisitCall(Call* expr) { // The receiver is either the global receiver or an object found // by LoadContextSlot. - EmitCall(expr); + EmitCallWithStub(expr); } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); { PreservePositionScope scope(masm()->positions_recorder()); VisitForStackValue(property->obj()); } if (property->key()->IsPropertyName()) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else { - EmitKeyedCallWithLoadIC(expr, property->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { @@ -2566,7 +2603,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ LoadRoot(x1, Heap::kUndefinedValueRootIndex); __ Push(x1); // Emit function call. - EmitCall(expr); + EmitCallWithStub(expr); } #ifdef DEBUG @@ -2616,7 +2653,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ LoadObject(x2, FeedbackVector()); __ Mov(x3, Smi::FromInt(expr->CallNewFeedbackSlot())); - CallConstructStub stub(RECORD_CONSTRUCTOR_TARGET); + CallConstructStub stub(RECORD_CALL_TARGET); __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(x0); diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index 53ef1a4742..e52cc868c3 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc @@ -444,7 +444,7 @@ void LCodeGen::DoCallNew(LCallNew* instr) { // No cell in x2 for construct type feedback in optimized code. __ LoadRoot(x2, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); ASSERT(ToRegister(instr->result()).is(x0)); diff --git a/src/ast.cc b/src/ast.cc index 6891bd74af..4781143d7d 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -594,11 +594,12 @@ void Expression::RecordToBooleanTypeFeedback(TypeFeedbackOracle* oracle) { int Call::ComputeFeedbackSlotCount(Isolate* isolate) { CallType call_type = GetCallType(isolate); - if (call_type == POSSIBLY_EVAL_CALL) { - return 0; + if (call_type == LOOKUP_SLOT_CALL || call_type == OTHER_CALL) { + // Call only uses a slot in some cases. + return 1; } - return 1; + return 0; } diff --git a/src/builtins.cc b/src/builtins.cc index a1a0a2729b..463a15f6c3 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1441,11 +1441,6 @@ static void Generate_KeyedStoreIC_SloppyArguments(MacroAssembler* masm) { #ifdef ENABLE_DEBUGGER_SUPPORT -static void Generate_CallICStub_DebugBreak(MacroAssembler* masm) { - Debug::GenerateCallICStubDebugBreak(masm); -} - - static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) { Debug::GenerateLoadICDebugBreak(masm); } @@ -1481,6 +1476,12 @@ static void Generate_CallFunctionStub_DebugBreak(MacroAssembler* masm) { } +static void Generate_CallFunctionStub_Recording_DebugBreak( + MacroAssembler* masm) { + Debug::GenerateCallFunctionStubRecordDebugBreak(masm); +} + + static void Generate_CallConstructStub_DebugBreak(MacroAssembler* masm) { Debug::GenerateCallConstructStubDebugBreak(masm); } diff --git a/src/builtins.h b/src/builtins.h index bb45ed50d9..88cfd53f48 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -204,12 +204,12 @@ enum BuiltinExtraArguments { DEBUG_BREAK) \ V(CallFunctionStub_DebugBreak, BUILTIN, DEBUG_STUB, \ DEBUG_BREAK) \ + V(CallFunctionStub_Recording_DebugBreak, BUILTIN, DEBUG_STUB, \ + DEBUG_BREAK) \ V(CallConstructStub_DebugBreak, BUILTIN, DEBUG_STUB, \ DEBUG_BREAK) \ V(CallConstructStub_Recording_DebugBreak, BUILTIN, DEBUG_STUB, \ DEBUG_BREAK) \ - V(CallICStub_DebugBreak, CALL_IC, DEBUG_STUB, \ - DEBUG_BREAK) \ V(LoadIC_DebugBreak, LOAD_IC, DEBUG_STUB, \ DEBUG_BREAK) \ V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_STUB, \ diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 157a3c22f8..06203629ae 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -501,11 +501,6 @@ Type* CompareNilICStub::GetInputType(Zone* zone, Handle map) { } -void CallICStub::PrintState(StringStream* stream) { - state_.Print(stream); -} - - void InstanceofStub::PrintName(StringStream* stream) { const char* args = ""; if (HasArgsInRegisters()) { @@ -588,6 +583,7 @@ void ArgumentsAccessStub::PrintName(StringStream* stream) { void CallFunctionStub::PrintName(StringStream* stream) { stream->Add("CallFunctionStub_Args%d", argc_); + if (RecordCallTarget()) stream->Add("_Recording"); } diff --git a/src/code-stubs.h b/src/code-stubs.h index 41431d1669..5a88942330 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -51,7 +51,6 @@ namespace internal { V(CompareIC) \ V(CompareNilIC) \ V(MathPow) \ - V(CallIC) \ V(FunctionPrototype) \ V(RecordWrite) \ V(StoreBufferOverflow) \ @@ -840,59 +839,6 @@ class ICStub: public PlatformCodeStub { }; -class CallICStub: public PlatformCodeStub { - public: - explicit CallICStub(const CallIC::State& state) - : state_(state) {} - - bool CallAsMethod() const { return state_.CallAsMethod(); } - bool IsGeneric() const { - return state_.IsGeneric(); - } - bool ArgumentsMustMatch() const { - return state_.ArgumentsMustMatch(); - } - bool IsSloppy() const { - return state_.IsSloppy(); - } - - int arg_count() const { return state_.arg_count(); } - - static int ExtractArgcFromMinorKey(int minor_key) { - CallIC::State state((ExtraICState) minor_key); - return state.arg_count(); - } - - virtual void Generate(MacroAssembler* masm); - - virtual Code::Kind GetCodeKind() const V8_OVERRIDE { - return Code::CALL_IC; - } - - virtual InlineCacheState GetICState() V8_FINAL V8_OVERRIDE { - return state_.GetICState(); - } - - virtual ExtraICState GetExtraICState() V8_FINAL V8_OVERRIDE { - return state_.GetExtraICState(); - } - - protected: - virtual int MinorKey() { return GetExtraICState(); } - virtual void PrintState(StringStream* stream) V8_FINAL V8_OVERRIDE; - - private: - virtual CodeStub::Major MajorKey() { return CallIC; } - - // Code generation helpers. - void GenerateMonomorphicCall(MacroAssembler* masm); - void GenerateSlowCall(MacroAssembler* masm); - void GenerateMiss(MacroAssembler* masm); - - CallIC::State state_; -}; - - class FunctionPrototypeStub: public ICStub { public: explicit FunctionPrototypeStub(Code::Kind kind) : ICStub(kind) { } @@ -1684,6 +1630,10 @@ class CallFunctionStub: public PlatformCodeStub { void Generate(MacroAssembler* masm); + virtual void FinishCode(Handle code) { + code->set_has_function_cache(RecordCallTarget()); + } + static int ExtractArgcFromMinorKey(int minor_key) { return ArgcBits::decode(minor_key); } @@ -1704,6 +1654,10 @@ class CallFunctionStub: public PlatformCodeStub { return FlagBits::encode(flags_) | ArgcBits::encode(argc_); } + bool RecordCallTarget() { + return flags_ == RECORD_CALL_TARGET; + } + bool CallAsMethod() { return flags_ == CALL_AS_METHOD || flags_ == WRAP_AND_CALL; } @@ -1716,7 +1670,7 @@ class CallFunctionStub: public PlatformCodeStub { class CallConstructStub: public PlatformCodeStub { public: - explicit CallConstructStub(CallConstructorFlags flags) : flags_(flags) {} + explicit CallConstructStub(CallFunctionFlags flags) : flags_(flags) {} void Generate(MacroAssembler* masm); @@ -1725,7 +1679,7 @@ class CallConstructStub: public PlatformCodeStub { } private: - CallConstructorFlags flags_; + CallFunctionFlags flags_; virtual void PrintName(StringStream* stream); @@ -1733,7 +1687,11 @@ class CallConstructStub: public PlatformCodeStub { int MinorKey() { return flags_; } bool RecordCallTarget() { - return (flags_ & RECORD_CONSTRUCTOR_TARGET) != 0; + return (flags_ & RECORD_CALL_TARGET) != 0; + } + + bool CallAsMethod() { + return (flags_ & CALL_AS_METHOD) != 0; } }; diff --git a/src/debug.cc b/src/debug.cc index 2b10d0c73d..83e78a22ba 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -406,7 +406,6 @@ bool BreakLocationIterator::IsStepInLocation(Isolate* isolate) { if (target_code->kind() == Code::STUB) { return target_code->major_key() == CodeStub::CallFunction; } - return target_code->is_call_stub(); } return false; } @@ -1426,9 +1425,6 @@ void Debug::PrepareStep(StepAction step_action, bool is_call_target = false; Address target = it.rinfo()->target_address(); Code* code = Code::GetCodeFromTargetAddress(target); - if (code->is_call_stub()) { - is_call_target = true; - } if (code->is_inline_cache_stub()) { is_inline_cache_stub = true; is_load_or_store = !is_call_target; @@ -1443,9 +1439,8 @@ void Debug::PrepareStep(StepAction step_action, maybe_call_function_stub = Code::GetCodeFromTargetAddress(original_target); } - if ((maybe_call_function_stub->kind() == Code::STUB && - maybe_call_function_stub->major_key() == CodeStub::CallFunction) || - maybe_call_function_stub->kind() == Code::CALL_IC) { + if (maybe_call_function_stub->kind() == Code::STUB && + maybe_call_function_stub->major_key() == CodeStub::CallFunction) { // Save reference to the code as we may need it to find out arguments // count for 'step in' later. call_function_stub = Handle(maybe_call_function_stub); @@ -1501,7 +1496,6 @@ void Debug::PrepareStep(StepAction step_action, } else if (!call_function_stub.is_null()) { // If it's CallFunction stub ensure target function is compiled and flood // it with one shot breakpoints. - bool is_call_ic = call_function_stub->kind() == Code::CALL_IC; // Find out number of arguments from the stub minor key. // Reverse lookup required as the minor key cannot be retrieved @@ -1517,13 +1511,11 @@ void Debug::PrepareStep(StepAction step_action, uint32_t key = Smi::cast(*obj)->value(); // Argc in the stub is the number of arguments passed - not the // expected arguments of the called function. - int call_function_arg_count = is_call_ic - ? CallICStub::ExtractArgcFromMinorKey(CodeStub::MinorKeyFromKey(key)) - : CallFunctionStub::ExtractArgcFromMinorKey( + int call_function_arg_count = + CallFunctionStub::ExtractArgcFromMinorKey( CodeStub::MinorKeyFromKey(key)); - - ASSERT(is_call_ic || - call_function_stub->major_key() == CodeStub::MajorKeyFromKey(key)); + ASSERT(call_function_stub->major_key() == + CodeStub::MajorKeyFromKey(key)); // Find target function on the expression stack. // Expression stack looks like this (top to bottom): @@ -1651,9 +1643,6 @@ Handle Debug::FindDebugBreak(Handle code, RelocInfo::Mode mode) { // used by the call site. if (code->is_inline_cache_stub()) { switch (code->kind()) { - case Code::CALL_IC: - return isolate->builtins()->CallICStub_DebugBreak(); - case Code::LOAD_IC: return isolate->builtins()->LoadIC_DebugBreak(); @@ -1682,7 +1671,11 @@ Handle Debug::FindDebugBreak(Handle code, RelocInfo::Mode mode) { } if (code->kind() == Code::STUB) { ASSERT(code->major_key() == CodeStub::CallFunction); - return isolate->builtins()->CallFunctionStub_DebugBreak(); + if (code->has_function_cache()) { + return isolate->builtins()->CallFunctionStub_Recording_DebugBreak(); + } else { + return isolate->builtins()->CallFunctionStub_DebugBreak(); + } } UNREACHABLE(); diff --git a/src/debug.h b/src/debug.h index 396956aa53..564f9e8854 100644 --- a/src/debug.h +++ b/src/debug.h @@ -431,7 +431,6 @@ class Debug { // Code generator routines. static void GenerateSlot(MacroAssembler* masm); - static void GenerateCallICStubDebugBreak(MacroAssembler* masm); static void GenerateLoadICDebugBreak(MacroAssembler* masm); static void GenerateStoreICDebugBreak(MacroAssembler* masm); static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm); @@ -439,6 +438,7 @@ class Debug { static void GenerateCompareNilICDebugBreak(MacroAssembler* masm); static void GenerateReturnDebugBreak(MacroAssembler* masm); static void GenerateCallFunctionStubDebugBreak(MacroAssembler* masm); + static void GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm); static void GenerateCallConstructStubDebugBreak(MacroAssembler* masm); static void GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm); static void GenerateSlotDebugBreak(MacroAssembler* masm); @@ -450,6 +450,9 @@ class Debug { // called, it only gets returned to. static void GenerateFrameDropperLiveEdit(MacroAssembler* masm); + // Called from stub-cache.cc. + static void GenerateCallICDebugBreak(MacroAssembler* masm); + // Describes how exactly a frame has been dropped from stack. enum FrameDropMode { // No frame has been dropped. diff --git a/src/full-codegen.h b/src/full-codegen.h index ea40ae45a6..0d0a6ffedc 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -122,14 +122,14 @@ class FullCodeGenerator: public AstVisitor { // Platform-specific code size multiplier. #if V8_TARGET_ARCH_IA32 - static const int kCodeSizeMultiplier = 116; + static const int kCodeSizeMultiplier = 100; #elif V8_TARGET_ARCH_X64 - static const int kCodeSizeMultiplier = 188; + static const int kCodeSizeMultiplier = 162; #elif V8_TARGET_ARCH_ARM - static const int kCodeSizeMultiplier = 165; + static const int kCodeSizeMultiplier = 142; #elif V8_TARGET_ARCH_ARM64 // TODO(all): Copied ARM value. Check this is sensible for ARM64. - static const int kCodeSizeMultiplier = 165; + static const int kCodeSizeMultiplier = 142; #elif V8_TARGET_ARCH_MIPS static const int kCodeSizeMultiplier = 142; #else @@ -485,9 +485,9 @@ class FullCodeGenerator: public AstVisitor { void EmitReturnSequence(); // Platform-specific code sequences for calls - void EmitCall(Call* expr, CallIC::CallType = CallIC::FUNCTION); - void EmitCallWithLoadIC(Call* expr); - void EmitKeyedCallWithLoadIC(Call* expr, Expression* key); + void EmitCallWithStub(Call* expr); + void EmitCallWithIC(Call* expr); + void EmitKeyedCallWithIC(Call* expr, Expression* key); // Platform-specific code for inline runtime calls. InlineFunctionGenerator FindInlineFunctionGenerator(Runtime::FunctionId id); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 12e4428304..785c5fd61c 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -540,7 +540,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, if (is_construct) { // No type feedback cell is available __ mov(ebx, masm->isolate()->factory()->undefined_value()); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ CallStub(&stub); } else { ParameterCount actual(eax); diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 7436678c1c..dca7ae72b6 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -2363,32 +2363,36 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) { } -static void GenericCallHelper(MacroAssembler* masm, - const CallIC::State& state, - bool wrap_and_call = false) { +void CallFunctionStub::Generate(MacroAssembler* masm) { + // ebx : feedback vector + // edx : (only if ebx is not the megamorphic symbol) slot in feedback + // vector (Smi) // edi : the function to call - - // wrap_and_call can only be true if we are compiling a monomorphic method. - ASSERT(!(wrap_and_call && state.IsGeneric())); - ASSERT(!wrap_and_call || state.CallAsMethod()); Isolate* isolate = masm->isolate(); Label slow, non_function, wrap, cont; - if (state.IsGeneric()) { + if (NeedsChecks()) { // Check that the function really is a JavaScript function. __ JumpIfSmi(edi, &non_function); // Goto slow case if we do not have a function. __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); __ j(not_equal, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + // Type information was updated. Because we may call Array, which + // expects either undefined or an AllocationSite in ebx we need + // to set ebx to undefined. + __ mov(ebx, Immediate(isolate->factory()->undefined_value())); + } } // Fast-case: Just invoke the function. - int argc = state.arg_count(); - ParameterCount actual(argc); + ParameterCount actual(argc_); - if (state.CallAsMethod()) { - if (state.IsGeneric()) { + if (CallAsMethod()) { + if (NeedsChecks()) { // Do not transform the receiver for strict mode functions. __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); __ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset), @@ -2401,39 +2405,41 @@ static void GenericCallHelper(MacroAssembler* masm, __ j(not_equal, &cont); } - if (state.IsGeneric() || state.IsSloppy() || wrap_and_call) { - // Load the receiver from the stack. - __ mov(eax, Operand(esp, (argc + 1) * kPointerSize)); + // Load the receiver from the stack. + __ mov(eax, Operand(esp, (argc_ + 1) * kPointerSize)); - if (state.IsGeneric()) { - __ JumpIfSmi(eax, &wrap); + if (NeedsChecks()) { + __ JumpIfSmi(eax, &wrap); - __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); - __ j(below, &wrap); - } else { - __ jmp(&wrap); - } + __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); + __ j(below, &wrap); + } else { + __ jmp(&wrap); } __ bind(&cont); } - if (state.ArgumentsMustMatch()) { - __ InvokeFunction(edi, actual, actual, JUMP_FUNCTION, NullCallWrapper()); - } else { - __ InvokeFunction(edi, actual, JUMP_FUNCTION, NullCallWrapper()); - } + __ InvokeFunction(edi, actual, JUMP_FUNCTION, NullCallWrapper()); - if (state.IsGeneric()) { + if (NeedsChecks()) { // Slow-case: Non-function called. __ bind(&slow); + if (RecordCallTarget()) { + // If there is a call target cache, mark it megamorphic in the + // non-function case. MegamorphicSentinel is an immortal immovable + // object (megamorphic symbol) so no write barrier is needed. + __ mov(FieldOperand(ebx, edx, times_half_pointer_size, + FixedArray::kHeaderSize), + Immediate(TypeFeedbackInfo::MegamorphicSentinel(isolate))); + } // Check for function proxy. __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE); __ j(not_equal, &non_function); __ pop(ecx); __ push(edi); // put proxy as additional argument under return address __ push(ecx); - __ Move(eax, Immediate(argc + 1)); + __ Move(eax, Immediate(argc_ + 1)); __ Move(ebx, Immediate(0)); __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY); { @@ -2444,25 +2450,16 @@ static void GenericCallHelper(MacroAssembler* masm, // CALL_NON_FUNCTION expects the non-function callee as receiver (instead // of the original receiver from the call site). __ bind(&non_function); - __ mov(Operand(esp, (argc + 1) * kPointerSize), edi); - __ Move(eax, Immediate(argc)); + __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi); + __ Move(eax, Immediate(argc_)); __ Move(ebx, Immediate(0)); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); Handle adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline(); __ jmp(adaptor, RelocInfo::CODE_TARGET); } - if (state.CallAsMethod()) { + if (CallAsMethod()) { __ bind(&wrap); - - if (!state.IsGeneric() && !wrap_and_call) { - // Do not transform the receiver for natives (shared already in ecx). - __ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ test_b(FieldOperand(ecx, SharedFunctionInfo::kNativeByteOffset), - 1 << SharedFunctionInfo::kNativeBitWithinByte); - __ j(not_equal, &cont); - } - // Wrap the receiver and patch it back onto the stack. { FrameScope frame_scope(masm, StackFrame::INTERNAL); __ push(edi); @@ -2470,36 +2467,12 @@ static void GenericCallHelper(MacroAssembler* masm, __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ pop(edi); } - __ mov(Operand(esp, (argc + 1) * kPointerSize), eax); + __ mov(Operand(esp, (argc_ + 1) * kPointerSize), eax); __ jmp(&cont); } } -void CallFunctionStub::Generate(MacroAssembler* masm) { - // edi : the function to call - - // GenericCallHelper expresses it's options in terms of CallIC::State. - CallIC::CallType call_type = CallAsMethod() ? - CallIC::METHOD : CallIC::FUNCTION; - - if (NeedsChecks()) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - argc_, - call_type)); - } else { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - argc_, - call_type, - CallIC::ARGUMENTS_COUNT_UNKNOWN, - SLOPPY), - true); - } -} - - void CallConstructStub::Generate(MacroAssembler* masm) { // eax : number of arguments // ebx : feedback vector @@ -2568,90 +2541,6 @@ void CallConstructStub::Generate(MacroAssembler* masm) { } -void CallICStub::GenerateMonomorphicCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - state_.arg_count(), - state_.call_type(), - state_.argument_check(), - state_.strict_mode())); -} - - -void CallICStub::GenerateSlowCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - state_.arg_count(), - state_.call_type())); -} - - -void CallICStub::Generate(MacroAssembler* masm) { - // edi - function - // ebx - vector - // edx - slot id - Isolate* isolate = masm->isolate(); - Label extra_checks_or_miss, slow; - - // The checks. First, does edi match the recorded monomorphic target? - __ cmp(edi, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - __ j(not_equal, &extra_checks_or_miss); - - GenerateMonomorphicCall(masm); - - __ bind(&extra_checks_or_miss); - if (IsGeneric()) { - Label miss_uninit; - - __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize)); - __ cmp(ecx, Immediate(TypeFeedbackInfo::MegamorphicSentinel(isolate))); - __ j(equal, &slow); - __ cmp(ecx, Immediate(TypeFeedbackInfo::UninitializedSentinel(isolate))); - __ j(equal, &miss_uninit); - // If we get here, go from monomorphic to megamorphic, Don't bother missing, - // just update. - __ mov(FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize), - Immediate(TypeFeedbackInfo::MegamorphicSentinel(isolate))); - __ jmp(&slow); - - __ bind(&miss_uninit); - } - - GenerateMiss(masm); - - // the slow case - __ bind(&slow); - GenerateSlowCall(masm); -} - - -void CallICStub::GenerateMiss(MacroAssembler* masm) { - // Get the receiver of the function from the stack; 1 ~ return address. - __ mov(ecx, Operand(esp, (state_.arg_count() + 1) * kPointerSize)); - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Push the receiver and the function and feedback info. - __ push(ecx); - __ push(edi); - __ push(ebx); - __ push(edx); - - // Call the entry. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCallIC_Miss), - masm->isolate()); - __ CallExternalReference(miss, 4); - - // Move result to edi and exit the internal frame. - __ mov(edi, eax); - } -} - - bool CEntryStub::NeedsImmovableCode() { return false; } diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc index 30a24b6a7c..42284ec75c 100644 --- a/src/ia32/debug-ia32.cc +++ b/src/ia32/debug-ia32.cc @@ -197,18 +197,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, } -void Debug::GenerateCallICStubDebugBreak(MacroAssembler* masm) { - // Register state for CallICStub - // ----------- S t a t e ------------- - // -- ebx : type feedback vector - // -- edx : type feedback slot (smi) - // -- edi : function - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, ebx.bit() | edx.bit() | edi.bit(), - 0, false); -} - - void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Register state for IC load call (from ic-ia32.cc). // ----------- S t a t e ------------- @@ -262,6 +250,15 @@ void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { + // Register state for keyed IC call call (from ic-ia32.cc) + // ----------- S t a t e ------------- + // -- ecx: name + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, ecx.bit(), 0, false); +} + + void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // Register state just before return from JS function (from codegen-ia32.cc). // ----------- S t a t e ------------- @@ -280,6 +277,18 @@ void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-ia32.cc). + // ----------- S t a t e ------------- + // -- ebx: feedback array + // -- edx: slot in feedback array + // -- edi: function + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, ebx.bit() | edx.bit() | edi.bit(), + 0, false); +} + + void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { // Register state for CallConstructStub (from code-stubs-ia32.cc). // eax is the actual number of arguments not encoded as a smi see comment diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 8e78a4e326..b021e46d3e 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -2582,15 +2582,17 @@ void FullCodeGenerator::CallIC(Handle code, } -// Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { - Expression* callee = expr->expression(); - CallIC::CallType call_type = callee->IsVariableProxy() - ? CallIC::FUNCTION - : CallIC::METHOD; + +// Code common for calls using the IC. +void FullCodeGenerator::EmitCallWithIC(Call* expr) { + Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + + CallFunctionFlags flags; // Get the target function. - if (call_type == CallIC::FUNCTION) { + if (callee->IsVariableProxy()) { { StackValueContext context(this); EmitVariableLoad(callee->AsVariableProxy()); PrepareForBailout(callee, NO_REGISTERS); @@ -2598,6 +2600,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push undefined as receiver. This is patched in the method prologue if it // is a sloppy mode method. __ push(Immediate(isolate()->factory()->undefined_value())); + flags = NO_CALL_FUNCTION_FLAGS; } else { // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2607,19 +2610,39 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push the target function under the receiver. __ push(Operand(esp, 0)); __ mov(Operand(esp, kPointerSize), eax); + flags = CALL_AS_METHOD; } - EmitCall(expr, call_type); + // Load the arguments. + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position of the IC call. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, flags); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + RecordJSReturnSite(expr); + + // Restore context register. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, eax); } // Code common for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, - Expression* key) { +void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, + Expression* key) { // Load the key. VisitForAccumulatorValue(key); Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2633,14 +2656,7 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, __ push(Operand(esp, 0)); __ mov(Operand(esp, kPointerSize), eax); - EmitCall(expr, CallIC::METHOD); -} - - -void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { // Load the arguments. - ZoneList* args = expr->arguments(); - int arg_count = args->length(); { PreservePositionScope scope(masm()->positions_recorder()); for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); @@ -2649,18 +2665,9 @@ void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { // Record source position of the IC call. SetSourcePosition(expr->position()); - Handle ic = CallIC::initialize_stub( - isolate(), arg_count, call_type); - Handle uninitialized = - TypeFeedbackInfo::UninitializedSentinel(isolate()); - StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); - __ LoadHeapObject(ebx, FeedbackVector()); - __ mov(edx, Immediate(Smi::FromInt(expr->CallFeedbackSlot()))); + CallFunctionStub stub(arg_count, CALL_AS_METHOD); __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); - // Don't assign a type feedback id to the IC, since type feedback is provided - // by the vector above. - CallIC(ic); - + __ CallStub(&stub); RecordJSReturnSite(expr); // Restore context register. @@ -2670,6 +2677,36 @@ void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { } +void FullCodeGenerator::EmitCallWithStub(Call* expr) { + // Code common for calls using the call stub. + ZoneList* args = expr->arguments(); + int arg_count = args->length(); + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + // Record source position for debugger. + SetSourcePosition(expr->position()); + + Handle uninitialized = + TypeFeedbackInfo::UninitializedSentinel(isolate()); + StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); + __ LoadHeapObject(ebx, FeedbackVector()); + __ mov(edx, Immediate(Smi::FromInt(expr->CallFeedbackSlot()))); + + // Record call targets in unoptimized code. + CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); + __ mov(edi, Operand(esp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + // Restore context register. + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + context()->DropAndPlug(1, eax); +} + + void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) { // Push copy of the first argument or undefined if it doesn't exist. if (arg_count > 0) { @@ -2738,7 +2775,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, eax); } else if (call_type == Call::GLOBAL_CALL) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else if (call_type == Call::LOOKUP_SLOT_CALL) { // Call to a lookup slot (dynamically introduced variable). @@ -2774,7 +2811,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // The receiver is either the global receiver or an object found by // LoadContextSlot. - EmitCall(expr); + EmitCallWithStub(expr); } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); @@ -2782,9 +2819,9 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(property->obj()); } if (property->key()->IsPropertyName()) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else { - EmitKeyedCallWithLoadIC(expr, property->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { @@ -2795,7 +2832,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { } __ push(Immediate(isolate()->factory()->undefined_value())); // Emit function call. - EmitCall(expr); + EmitCallWithStub(expr); } #ifdef DEBUG @@ -2845,7 +2882,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ LoadHeapObject(ebx, FeedbackVector()); __ mov(edx, Immediate(Smi::FromInt(expr->CallNewFeedbackSlot()))); - CallConstructStub stub(RECORD_CONSTRUCTOR_TARGET); + CallConstructStub stub(RECORD_CALL_TARGET); __ call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(eax); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 778599c770..512cee4bab 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -4283,7 +4283,7 @@ void LCodeGen::DoCallNew(LCallNew* instr) { // No cell in ebx for construct type feedback in optimized code __ mov(ebx, isolate()->factory()->undefined_value()); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ Move(eax, Immediate(instr->arity())); CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); } diff --git a/src/ic.cc b/src/ic.cc index 688908ead2..8e2d589c1e 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -91,11 +91,9 @@ void IC::TraceIC(const char* type, } JavaScriptFrame::PrintTop(isolate(), stdout, false, true); ExtraICState extra_state = new_target->extra_ic_state(); - const char* modifier = ""; - if (new_target->kind() == Code::KEYED_STORE_IC) { - modifier = GetTransitionMarkModifier( - KeyedStoreIC::GetKeyedAccessStoreMode(extra_state)); - } + const char* modifier = + GetTransitionMarkModifier( + KeyedStoreIC::GetKeyedAccessStoreMode(extra_state)); PrintF(" (%c->%c%s)", TransitionMarkFromState(state()), TransitionMarkFromState(new_state), @@ -417,10 +415,6 @@ void IC::PostPatching(Address address, Code* target, Code* old_target) { target->is_inline_cache_stub()) { int delta = ComputeTypeInfoCountDelta(old_target->ic_state(), target->ic_state()); - // Call ICs don't have interesting state changes from this point - // of view. - ASSERT(target->kind() != Code::CALL_IC || delta == 0); - // Not all Code objects have TypeFeedbackInfo. if (host->type_feedback_info()->IsTypeFeedbackInfo() && delta != 0) { TypeFeedbackInfo* info = @@ -457,8 +451,6 @@ void IC::Clear(Isolate* isolate, Address address, return StoreIC::Clear(isolate, address, target, constant_pool); case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(isolate, address, target, constant_pool); - case Code::CALL_IC: - return CallIC::Clear(isolate, address, target, constant_pool); case Code::COMPARE_IC: return CompareIC::Clear(isolate, address, target, constant_pool); case Code::COMPARE_NIL_IC: @@ -485,25 +477,6 @@ void KeyedLoadIC::Clear(Isolate* isolate, } -void CallIC::Clear(Isolate* isolate, - Address address, - Code* target, - ConstantPoolArray* constant_pool) { - // CallIC just has a generic stub and a monomorphic stub. Only clear if we - // are monomorphic - if (target->ic_state() != ::v8::internal::MONOMORPHIC) return; - - CallIC::State existing_state(target->extra_ic_state()); - - // Install default stub with the immutable parts of existing state. - HandleScope scope(isolate); - CallICStub stub(State::DefaultCallState(existing_state.arg_count(), - existing_state.call_type())); - Code* code = *stub.GetCode(isolate); - SetTargetAtAddress(address, code, constant_pool); -} - - void LoadIC::Clear(Isolate* isolate, Address address, Code* target, @@ -1303,32 +1276,6 @@ MaybeObject* StoreIC::Store(Handle object, } -void CallIC::State::Print(StringStream* stream) const { - stream->Add("(args(%d), ", - argc_); - stream->Add("%s, ", - call_type_ == CallIC::METHOD ? "METHOD" : "FUNCTION"); - stream->Add("%s, ", - stub_type_ == CallIC::MONOMORPHIC ? - "MONOMORPHIC" : "NOT_MONOMORPHIC"); - stream->Add("%s, ", - argument_check_ == CallIC::ARGUMENTS_MUST_MATCH ? - "args_must_match" : "args_might_match"); - stream->Add("%s)", - strict_mode_ == STRICT ? - "strict" : "sloppy"); -} - - -Handle CallIC::initialize_stub(Isolate* isolate, - int argc, - CallType call_type) { - CallICStub stub(State::DefaultCallState(argc, call_type)); - Handle code = stub.GetCode(isolate); - return code; -} - - Handle StoreIC::initialize_stub(Isolate* isolate, StrictMode strict_mode) { ExtraICState extra_state = ComputeExtraICState(strict_mode); @@ -1801,102 +1748,6 @@ MaybeObject* KeyedStoreIC::Store(Handle object, } -CallIC::State::State(ExtraICState extra_ic_state) - : argc_(ArgcBits::decode(extra_ic_state)), - call_type_(CallTypeBits::decode(extra_ic_state)), - stub_type_(StubTypeBits::decode(extra_ic_state)), - argument_check_(ArgumentCheckBits::decode(extra_ic_state)), - strict_mode_(StrictModeBits::decode(extra_ic_state)) { -} - - -ExtraICState CallIC::State::GetExtraICState() const { - ExtraICState extra_ic_state = - ArgcBits::encode(argc_) | - CallTypeBits::encode(call_type_) | - StubTypeBits::encode(stub_type_) | - ArgumentCheckBits::encode(argument_check_) | - StrictModeBits::encode(strict_mode_); - return extra_ic_state; -} - - -CallIC::State CallIC::State::ToGenericState() { - if (stub_type() == CallIC::MONOMORPHIC) { - return DefaultCallState(arg_count(), call_type()); - } - return *this; -} - - -CallIC::State CallIC::State::ToMonomorphicState( - Handle function) { - // Choose the right monomorphic handler - SharedFunctionInfo* shared = function->shared(); - ArgumentCheck new_argument_check = - shared->formal_parameter_count() == arg_count() - ? CallIC::ARGUMENTS_MUST_MATCH - : CallIC::ARGUMENTS_COUNT_UNKNOWN; - StrictMode new_strict_mode = shared->strict_mode(); - if (new_argument_check != argument_check() || - new_strict_mode != strict_mode()) { - return MonomorphicCallState(arg_count(), call_type(), new_argument_check, - new_strict_mode); - } - - return *this; -} - - -void CallIC::HandleMiss(Handle receiver, - Handle function, - Handle vector, - Handle slot) { - State state(target()->extra_ic_state()); - Object* feedback = vector->get(slot->value()); - - if (feedback->IsJSFunction() || !function->IsJSFunction()) { - // We are going generic. - ASSERT(!function->IsJSFunction() || *function != feedback); - - vector->set(slot->value(), - *TypeFeedbackInfo::MegamorphicSentinel(isolate())); - - // We only need to patch if we currently don't have the default stub in - // place. - State new_state = state.ToGenericState(); - if (new_state != state) { - CallICStub stub(new_state); - set_target(*stub.GetCode(isolate())); - TRACE_GENERIC_IC(isolate(), "CallIC", "generic"); - } - } else { - // If we came here feedback must be the uninitialized sentinel, - // and we are going monomorphic. - ASSERT(feedback == *TypeFeedbackInfo::UninitializedSentinel(isolate())); - ASSERT(state.stub_type() != CallIC::MONOMORPHIC); - - vector->set(slot->value(), *function); - - // Choose the right monomorphic handler - Handle js_function = Handle::cast(function); - State new_state = state.ToMonomorphicState(js_function); - if (new_state != state) { - CallICStub stub(new_state); - Handle code = stub.GetCode(isolate()); - // Creating the code above could result in a gc, which clears the type - // vector. If that happens, our plan to go monomorphic has been - // pre-empted. - feedback = vector->get(slot->value()); - if (feedback == *function) { - set_target(*code); - TRACE_IC("CallIC", handle(js_function->shared()->name(), isolate())); - } - } - } -} - - #undef TRACE_IC @@ -1905,19 +1756,6 @@ void CallIC::HandleMiss(Handle receiver, // // Used from ic-.cc. -RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) { - HandleScope scope(isolate); - ASSERT(args.length() == 4); - CallIC ic(isolate); - Handle receiver = args.at(0); - Handle function = args.at(1); - Handle vector = args.at(2); - Handle slot = args.at(3); - ic.HandleMiss(receiver, function, vector, slot); - return *function; -} - - // Used from ic-.cc. RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) { HandleScope scope(isolate); diff --git a/src/ic.h b/src/ic.h index 904ab184d2..41882e6689 100644 --- a/src/ic.h +++ b/src/ic.h @@ -42,7 +42,6 @@ const int kMaxKeyedPolymorphism = 4; #define IC_UTIL_LIST(ICU) \ ICU(LoadIC_Miss) \ ICU(KeyedLoadIC_Miss) \ - ICU(CallIC_Miss) \ ICU(StoreIC_Miss) \ ICU(StoreIC_ArrayLength) \ ICU(StoreIC_Slow) \ @@ -120,10 +119,6 @@ class IC { bool IsStoreStub() const { return target()->is_store_stub() || target()->is_keyed_store_stub(); } - - bool IsCallStub() const { - return target()->is_call_stub(); - } #endif // Determines which map must be used for keeping the code stub. @@ -344,128 +339,6 @@ class IC_Utility { }; -class CallIC: public IC { - public: - enum CallType { METHOD, FUNCTION }; - enum StubType { DEFAULT, MONOMORPHIC }; - enum ArgumentCheck { ARGUMENTS_MUST_MATCH, ARGUMENTS_COUNT_UNKNOWN }; - - class State V8_FINAL BASE_EMBEDDED { - public: - explicit State(ExtraICState extra_ic_state); - - static State MonomorphicCallState(int argc, CallType call_type, - ArgumentCheck argument_check, - StrictMode strict_mode) { - return State(argc, call_type, MONOMORPHIC, argument_check, strict_mode); - } - - static State SlowCallState(int argc, CallType call_type) { - return State(argc, call_type, DEFAULT, ARGUMENTS_COUNT_UNKNOWN, - SLOPPY); - } - - static State DefaultCallState(int argc, CallType call_type) { - return State(argc, call_type, DEFAULT, ARGUMENTS_MUST_MATCH, - SLOPPY); - } - - // Transition from the current state to another. - State ToGenericState(); - State ToMonomorphicState(Handle function); - - InlineCacheState GetICState() const { - return stub_type_ == CallIC::MONOMORPHIC - ? ::v8::internal::MONOMORPHIC - : ::v8::internal::GENERIC; - } - - ExtraICState GetExtraICState() const; - - static void GenerateAheadOfTime( - Isolate*, void (*Generate)(Isolate*, const State&)); - - int arg_count() const { return argc_; } - CallType call_type() const { return call_type_; } - StubType stub_type() const { return stub_type_; } - ArgumentCheck argument_check() const { return argument_check_; } - StrictMode strict_mode() const { - return strict_mode_; - } - - bool ArgumentsMustMatch() const { - return argument_check_ == ARGUMENTS_MUST_MATCH; - } - bool IsGeneric() const { return stub_type_ == DEFAULT; } - bool CallAsMethod() const { return call_type_ == METHOD; } - bool IsSloppy() const { - return strict_mode_ == SLOPPY; - } - - void Print(StringStream* stream) const; - - bool operator==(const State& other_state) const { - return (argc_ == other_state.argc_ && - call_type_ == other_state.call_type_ && - stub_type_ == other_state.stub_type_ && - argument_check_ == other_state.argument_check_ && - strict_mode_ == other_state.strict_mode_); - } - - bool operator!=(const State& other_state) const { - return !(*this == other_state); - } - - private: - State(int argc, - CallType call_type, - StubType stub_type, - ArgumentCheck argument_check, - StrictMode strict_mode) - : argc_(argc), - call_type_(call_type), - stub_type_(stub_type), - argument_check_(argument_check), - strict_mode_(strict_mode) { - } - - class ArgcBits: public BitField {}; - class CallTypeBits: public BitField {}; - class StubTypeBits: - public BitField {}; // NOLINT - class ArgumentCheckBits: - public BitField {}; // NOLINT - class StrictModeBits: - public BitField {}; // NOLINT - - const int argc_; - const CallType call_type_; - const StubType stub_type_; - const ArgumentCheck argument_check_; - const StrictMode strict_mode_; - }; - - explicit CallIC(Isolate* isolate) - : IC(EXTRA_CALL_FRAME, isolate) { - } - - void HandleMiss(Handle receiver, - Handle function, - Handle vector, - Handle slot); - - // Code generator routines. - static Handle initialize_stub(Isolate* isolate, - int argc, - CallType call_type); - - static void Clear(Isolate* isolate, Address address, Code* target, - ConstantPoolArray* constant_pool); -}; - - class LoadIC: public IC { public: // ExtraICState bits diff --git a/src/log.cc b/src/log.cc index 41a924cf29..942170c283 100644 --- a/src/log.cc +++ b/src/log.cc @@ -1872,10 +1872,6 @@ void Logger::LogCodeObject(Object* object) { description = "A load IC from the snapshot"; tag = Logger::LOAD_IC_TAG; break; - case Code::CALL_IC: - description = "A call IC from the snapshot"; - tag = Logger::CALL_IC_TAG; - break; case Code::STORE_IC: description = "A store IC from the snapshot"; tag = Logger::STORE_IC_TAG; diff --git a/src/log.h b/src/log.h index 8b7d0bb194..c01aca273a 100644 --- a/src/log.h +++ b/src/log.h @@ -144,7 +144,6 @@ struct TickSample; V(KEYED_STORE_POLYMORPHIC_IC_TAG, "KeyedStorePolymorphicIC") \ V(KEYED_EXTERNAL_ARRAY_STORE_IC_TAG, "KeyedExternalArrayStoreIC") \ V(LAZY_COMPILE_TAG, "LazyCompile") \ - V(CALL_IC_TAG, "CallIC") \ V(LOAD_IC_TAG, "LoadIC") \ V(LOAD_POLYMORPHIC_IC_TAG, "LoadPolymorphicIC") \ V(REG_EXP_TAG, "RegExp") \ diff --git a/src/objects-inl.h b/src/objects-inl.h index ee6b94d908..978704ac54 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -4433,7 +4433,6 @@ bool Code::has_major_key() { kind() == LOAD_IC || kind() == KEYED_LOAD_IC || kind() == STORE_IC || - kind() == CALL_IC || kind() == KEYED_STORE_IC || kind() == TO_BOOLEAN_IC; } @@ -5878,7 +5877,7 @@ void Code::set_type_feedback_info(Object* value, WriteBarrierMode mode) { int Code::stub_info() { ASSERT(kind() == COMPARE_IC || kind() == COMPARE_NIL_IC || - kind() == BINARY_OP_IC || kind() == LOAD_IC || kind() == CALL_IC); + kind() == BINARY_OP_IC || kind() == LOAD_IC); return Smi::cast(raw_type_feedback_info())->value(); } @@ -5889,7 +5888,6 @@ void Code::set_stub_info(int value) { kind() == BINARY_OP_IC || kind() == STUB || kind() == LOAD_IC || - kind() == CALL_IC || kind() == KEYED_LOAD_IC || kind() == STORE_IC || kind() == KEYED_STORE_IC); diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h index 5f8241c20a..bea201a6f3 100644 --- a/src/objects-visiting-inl.h +++ b/src/objects-visiting-inl.h @@ -309,16 +309,9 @@ void StaticMarkingVisitor::VisitCodeTarget( // Monomorphic ICs are preserved when possible, but need to be flushed // when they might be keeping a Context alive, or when the heap is about // to be serialized. - - // TODO(mvstanton): CALL_IC in monomorphic state needs to be cleared because - // it's state is synced with a type feedback slot, which is always cleared on - // gc. If we leave it alone, we'll end up in a hybrid of (cleared feedback - // slot but monomorphic IC), which is complex. if (FLAG_cleanup_code_caches_at_gc && target->is_inline_cache_stub() && (target->ic_state() == MEGAMORPHIC || target->ic_state() == GENERIC || target->ic_state() == POLYMORPHIC || heap->flush_monomorphic_ics() || - (target->ic_state() == MONOMORPHIC && - target->kind() == Code::CALL_IC) || Serializer::enabled() || target->ic_age() != heap->global_ic_age())) { IC::Clear(target->GetIsolate(), rinfo->pc(), rinfo->host()->constant_pool()); diff --git a/src/objects.cc b/src/objects.cc index 3065874fc3..ce0aa6d92e 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -10199,9 +10199,6 @@ void SharedFunctionInfo::AttachInitialMap(Map* map) { void SharedFunctionInfo::ResetForNewContext(int new_ic_age) { code()->ClearInlineCaches(); - // If we clear ICs, we need to clear the type feedback vector too, since - // CallICs are synced with a feedback vector slot. - code()->ClearTypeFeedbackInfo(map()->GetHeap()); set_ic_age(new_ic_age); if (code()->kind() == Code::FUNCTION) { code()->set_profiler_ticks(0); diff --git a/src/objects.h b/src/objects.h index 6788962cf4..948246f2e4 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5199,7 +5199,6 @@ class Code: public HeapObject { #define IC_KIND_LIST(V) \ V(LOAD_IC) \ V(KEYED_LOAD_IC) \ - V(CALL_IC) \ V(STORE_IC) \ V(KEYED_STORE_IC) \ V(BINARY_OP_IC) \ @@ -5309,7 +5308,6 @@ class Code: public HeapObject { inline bool is_keyed_load_stub() { return kind() == KEYED_LOAD_IC; } inline bool is_store_stub() { return kind() == STORE_IC; } inline bool is_keyed_store_stub() { return kind() == KEYED_STORE_IC; } - inline bool is_call_stub() { return kind() == CALL_IC; } inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; } inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; } inline bool is_compare_nil_ic_stub() { return kind() == COMPARE_NIL_IC; } diff --git a/src/v8globals.h b/src/v8globals.h index 3724097d8b..8a89a933a7 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -283,6 +283,8 @@ enum InlineCacheState { enum CallFunctionFlags { NO_CALL_FUNCTION_FLAGS, + // The call target is cached in the instruction stream. + RECORD_CALL_TARGET, CALL_AS_METHOD, // Always wrap the receiver and call to the JSFunction. Only use this flag // both the receiver type and the target method are statically known. @@ -290,13 +292,6 @@ enum CallFunctionFlags { }; -enum CallConstructorFlags { - NO_CALL_CONSTRUCTOR_FLAGS, - // The call target is cached in the instruction stream. - RECORD_CONSTRUCTOR_TARGET -}; - - enum InlineCacheHolderFlag { OWN_MAP, // For fast properties objects. PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 404090dc5a..66ac55670d 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -600,7 +600,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm, // No type feedback cell is available __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex); // Expects rdi to hold function pointer. - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); __ CallStub(&stub); } else { ParameterCount actual(rax); diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 58147e2a99..11699e9f9c 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2197,33 +2197,37 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) { } -static void GenericCallHelper(MacroAssembler* masm, - const CallIC::State& state, - bool wrap_and_call = false) { +void CallFunctionStub::Generate(MacroAssembler* masm) { + // rbx : feedback vector + // rdx : (only if rbx is not the megamorphic symbol) slot in feedback + // vector (Smi) // rdi : the function to call - - // wrap_and_call can only be true if we are compiling a monomorphic method. - ASSERT(!(wrap_and_call && state.IsGeneric())); - ASSERT(!wrap_and_call || state.CallAsMethod()); Isolate* isolate = masm->isolate(); Label slow, non_function, wrap, cont; - int argc = state.arg_count(); - StackArgumentsAccessor args(rsp, argc); + StackArgumentsAccessor args(rsp, argc_); - if (state.IsGeneric()) { + if (NeedsChecks()) { // Check that the function really is a JavaScript function. __ JumpIfSmi(rdi, &non_function); // Goto slow case if we do not have a function. __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); __ j(not_equal, &slow); + + if (RecordCallTarget()) { + GenerateRecordCallTarget(masm); + // Type information was updated. Because we may call Array, which + // expects either undefined or an AllocationSite in rbx we need + // to set rbx to undefined. + __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex); + } } // Fast-case: Just invoke the function. - ParameterCount actual(argc); + ParameterCount actual(argc_); - if (state.CallAsMethod()) { - if (state.IsGeneric()) { + if (CallAsMethod()) { + if (NeedsChecks()) { // Do not transform the receiver for strict mode functions. __ movp(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); __ testb(FieldOperand(rcx, SharedFunctionInfo::kStrictModeByteOffset), @@ -2237,39 +2241,43 @@ static void GenericCallHelper(MacroAssembler* masm, __ j(not_equal, &cont); } - if (state.IsGeneric() || state.IsSloppy() || wrap_and_call) { - // Load the receiver from the stack. - __ movp(rax, args.GetReceiverOperand()); - if (state.IsGeneric()) { - __ JumpIfSmi(rax, &wrap); + // Load the receiver from the stack. + __ movp(rax, args.GetReceiverOperand()); - __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); - __ j(below, &wrap); - } else { - __ jmp(&wrap); - } + if (NeedsChecks()) { + __ JumpIfSmi(rax, &wrap); + + __ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx); + __ j(below, &wrap); + } else { + __ jmp(&wrap); } __ bind(&cont); } + __ InvokeFunction(rdi, actual, JUMP_FUNCTION, NullCallWrapper()); - if (state.ArgumentsMustMatch()) { - __ InvokeFunction(rdi, actual, actual, JUMP_FUNCTION, NullCallWrapper()); - } else { - __ InvokeFunction(rdi, actual, JUMP_FUNCTION, NullCallWrapper()); - } - - if (state.IsGeneric()) { + if (NeedsChecks()) { // Slow-case: Non-function called. __ bind(&slow); + if (RecordCallTarget()) { + // If there is a call target cache, mark it megamorphic in the + // non-function case. MegamorphicSentinel is an immortal immovable + // object (megamorphic symbol) so no write barrier is needed. + __ SmiToInteger32(rdx, rdx); + __ Move(FieldOperand(rbx, rdx, times_pointer_size, + FixedArray::kHeaderSize), + TypeFeedbackInfo::MegamorphicSentinel(isolate)); + __ Integer32ToSmi(rdx, rdx); + } // Check for function proxy. __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE); __ j(not_equal, &non_function); __ PopReturnAddressTo(rcx); __ Push(rdi); // put proxy as additional argument under return address __ PushReturnAddressFrom(rcx); - __ Set(rax, argc + 1); + __ Set(rax, argc_ + 1); __ Set(rbx, 0); __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY); { @@ -2282,7 +2290,7 @@ static void GenericCallHelper(MacroAssembler* masm, // of the original receiver from the call site). __ bind(&non_function); __ movp(args.GetReceiverOperand(), rdi); - __ Set(rax, argc); + __ Set(rax, argc_); __ Set(rbx, 0); __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); Handle adaptor = @@ -2290,17 +2298,8 @@ static void GenericCallHelper(MacroAssembler* masm, __ Jump(adaptor, RelocInfo::CODE_TARGET); } - if (state.CallAsMethod()) { + if (CallAsMethod()) { __ bind(&wrap); - - if (!state.IsGeneric() && !wrap_and_call) { - // Do not transform the receiver for natives. - __ movp(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); - __ testb(FieldOperand(rcx, SharedFunctionInfo::kNativeByteOffset), - Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte)); - __ j(not_equal, &cont); - } - // Wrap the receiver and patch it back onto the stack. { FrameScope frame_scope(masm, StackFrame::INTERNAL); __ Push(rdi); @@ -2314,30 +2313,6 @@ static void GenericCallHelper(MacroAssembler* masm, } -void CallFunctionStub::Generate(MacroAssembler* masm) { - // rdi : the function to call - - // GenericCallHelper expresses it's options in terms of CallIC::State. - CallIC::CallType call_type = CallAsMethod() ? - CallIC::METHOD : CallIC::FUNCTION; - - if (NeedsChecks()) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - argc_, - call_type)); - } else { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - argc_, - call_type, - CallIC::ARGUMENTS_COUNT_UNKNOWN, - SLOPPY), - true); - } -} - - void CallConstructStub::Generate(MacroAssembler* masm) { // rax : number of arguments // rbx : feedback vector @@ -2404,92 +2379,6 @@ void CallConstructStub::Generate(MacroAssembler* masm) { } -void CallICStub::GenerateMonomorphicCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::MonomorphicCallState( - state_.arg_count(), - state_.call_type(), - state_.argument_check(), - state_.strict_mode())); -} - - -void CallICStub::GenerateSlowCall(MacroAssembler* masm) { - GenericCallHelper(masm, - CallIC::State::SlowCallState( - state_.arg_count(), - state_.call_type())); -} - - -void CallICStub::Generate(MacroAssembler* masm) { - // rdi - function - // rbx - vector - // rdx - slot id - Isolate* isolate = masm->isolate(); - Label extra_checks_or_miss, slow; - - // The checks. First, does edi match the recorded monomorphic target? - __ SmiToInteger32(rdx, rdx); - __ cmpq(rdi, FieldOperand(rbx, rdx, times_pointer_size, - FixedArray::kHeaderSize)); - __ j(not_equal, &extra_checks_or_miss); - - GenerateMonomorphicCall(masm); - - __ bind(&extra_checks_or_miss); - if (IsGeneric()) { - Label miss_uninit; - - __ movp(rcx, FieldOperand(rbx, rdx, times_pointer_size, - FixedArray::kHeaderSize)); - __ Cmp(rcx, TypeFeedbackInfo::MegamorphicSentinel(isolate)); - __ j(equal, &slow); - __ Cmp(rcx, TypeFeedbackInfo::UninitializedSentinel(isolate)); - __ j(equal, &miss_uninit); - // If we get here, go from monomorphic to megamorphic, Don't bother missing, - // just update. - __ Move(FieldOperand(rbx, rdx, times_pointer_size, - FixedArray::kHeaderSize), - TypeFeedbackInfo::MegamorphicSentinel(isolate)); - __ jmp(&slow); - - __ bind(&miss_uninit); - } - - GenerateMiss(masm); - - // the slow case - __ bind(&slow); - GenerateSlowCall(masm); -} - - -void CallICStub::GenerateMiss(MacroAssembler* masm) { - // Get the receiver of the function from the stack; 1 ~ return address. - __ movp(rcx, Operand(rsp, (state_.arg_count() + 1) * kPointerSize)); - - { - FrameScope scope(masm, StackFrame::INTERNAL); - - // Push the receiver and the function and feedback info. - __ Push(rcx); - __ Push(rdi); - __ Push(rbx); - __ Integer32ToSmi(rdx, rdx); - __ Push(rdx); - - // Call the entry. - ExternalReference miss = ExternalReference(IC_Utility(IC::kCallIC_Miss), - masm->isolate()); - __ CallExternalReference(miss, 4); - - // Move result to edi and exit the internal frame. - __ movp(rdi, rax); - } -} - - bool CEntryStub::NeedsImmovableCode() { return false; } diff --git a/src/x64/debug-x64.cc b/src/x64/debug-x64.cc index c8303b236e..36d5df678e 100644 --- a/src/x64/debug-x64.cc +++ b/src/x64/debug-x64.cc @@ -177,18 +177,6 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, } -void Debug::GenerateCallICStubDebugBreak(MacroAssembler* masm) { - // Register state for CallICStub - // ----------- S t a t e ------------- - // -- rbx : type feedback vector - // -- rdx : type feedback slot (smi) - // -- rdi : function - // ----------------------------------- - Generate_DebugBreakCallHelper(masm, rbx.bit() | rdx.bit() | rdi.bit(), - 0, false); -} - - void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Register state for IC load call (from ic-x64.cc). // ----------- S t a t e ------------- @@ -242,6 +230,15 @@ void Debug::GenerateCompareNilICDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) { + // Register state for IC call call (from ic-x64.cc) + // ----------- S t a t e ------------- + // -- rcx: function name + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false); +} + + void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) { // Register state just before return from JS function (from codegen-x64.cc). // ----------- S t a t e ------------- @@ -260,6 +257,18 @@ void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) { } +void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) { + // Register state for CallFunctionStub (from code-stubs-x64.cc). + // ----------- S t a t e ------------- + // -- rdi : function + // -- rbx: feedback array + // -- rdx: slot in feedback array + // ----------------------------------- + Generate_DebugBreakCallHelper(masm, rbx.bit() | rdx.bit() | rdi.bit(), + 0, false); +} + + void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) { // Register state for CallConstructStub (from code-stubs-x64.cc). // rax is the actual number of arguments not encoded as a smi, see comment diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 277ce061e7..e4fa4d9bda 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -2573,14 +2573,14 @@ void FullCodeGenerator::CallIC(Handle code, // Code common for calls using the IC. -void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { +void FullCodeGenerator::EmitCallWithIC(Call* expr) { Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); - CallIC::CallType call_type = callee->IsVariableProxy() - ? CallIC::FUNCTION - : CallIC::METHOD; - // Get the target function. - if (call_type == CallIC::FUNCTION) { + CallFunctionFlags flags; + // Get the target function; + if (callee->IsVariableProxy()) { { StackValueContext context(this); EmitVariableLoad(callee->AsVariableProxy()); PrepareForBailout(callee, NO_REGISTERS); @@ -2588,6 +2588,7 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push undefined as receiver. This is patched in the method prologue if it // is a sloppy mode method. __ Push(isolate()->factory()->undefined_value()); + flags = NO_CALL_FUNCTION_FLAGS; } else { // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2597,19 +2598,40 @@ void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) { // Push the target function under the receiver. __ Push(Operand(rsp, 0)); __ movp(Operand(rsp, kPointerSize), rax); + flags = CALL_AS_METHOD; } - EmitCall(expr, call_type); + // Load the arguments. + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, flags); + __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + + // Restore context register. + __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, rax); } // Common code for calls using the IC. -void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, - Expression* key) { +void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, + Expression* key) { // Load the key. VisitForAccumulatorValue(key); Expression* callee = expr->expression(); + ZoneList* args = expr->arguments(); + int arg_count = args->length(); // Load the function from the receiver. ASSERT(callee->IsProperty()); @@ -2621,12 +2643,29 @@ void FullCodeGenerator::EmitKeyedCallWithLoadIC(Call* expr, __ Push(Operand(rsp, 0)); __ movp(Operand(rsp, kPointerSize), rax); - EmitCall(expr, CallIC::METHOD); + // Load the arguments. + { PreservePositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + } + + // Record source position for debugger. + SetSourcePosition(expr->position()); + CallFunctionStub stub(arg_count, CALL_AS_METHOD); + __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); + __ CallStub(&stub); + + RecordJSReturnSite(expr); + // Restore context register. + __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + + context()->DropAndPlug(1, rax); } -void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { - // Load the arguments. +void FullCodeGenerator::EmitCallWithStub(Call* expr) { + // Code common for calls using the call stub. ZoneList* args = expr->arguments(); int arg_count = args->length(); { PreservePositionScope scope(masm()->positions_recorder()); @@ -2634,23 +2673,20 @@ void FullCodeGenerator::EmitCall(Call* expr, CallIC::CallType call_type) { VisitForStackValue(args->at(i)); } } - - // Record source position of the IC call. + // Record source position for debugger. SetSourcePosition(expr->position()); - Handle ic = CallIC::initialize_stub( - isolate(), arg_count, call_type); + Handle uninitialized = TypeFeedbackInfo::UninitializedSentinel(isolate()); StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized); __ Move(rbx, FeedbackVector()); __ Move(rdx, Smi::FromInt(expr->CallFeedbackSlot())); + + // Record call targets in unoptimized code. + CallFunctionStub stub(arg_count, RECORD_CALL_TARGET); __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize)); - // Don't assign a type feedback id to the IC, since type feedback is provided - // by the vector above. - CallIC(ic); - + __ CallStub(&stub); RecordJSReturnSite(expr); - // Restore context register. __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); // Discard the function left on TOS. @@ -2727,7 +2763,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); context()->DropAndPlug(1, rax); } else if (call_type == Call::GLOBAL_CALL) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else if (call_type == Call::LOOKUP_SLOT_CALL) { // Call to a lookup slot (dynamically introduced variable). @@ -2764,16 +2800,16 @@ void FullCodeGenerator::VisitCall(Call* expr) { // The receiver is either the global receiver or an object found by // LoadContextSlot. - EmitCall(expr); + EmitCallWithStub(expr); } else if (call_type == Call::PROPERTY_CALL) { Property* property = callee->AsProperty(); { PreservePositionScope scope(masm()->positions_recorder()); VisitForStackValue(property->obj()); } if (property->key()->IsPropertyName()) { - EmitCallWithLoadIC(expr); + EmitCallWithIC(expr); } else { - EmitKeyedCallWithLoadIC(expr, property->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { ASSERT(call_type == Call::OTHER_CALL); @@ -2783,7 +2819,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { } __ PushRoot(Heap::kUndefinedValueRootIndex); // Emit function call. - EmitCall(expr); + EmitCallWithStub(expr); } #ifdef DEBUG @@ -2833,7 +2869,7 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) { __ Move(rbx, FeedbackVector()); __ Move(rdx, Smi::FromInt(expr->CallNewFeedbackSlot())); - CallConstructStub stub(RECORD_CONSTRUCTOR_TARGET); + CallConstructStub stub(RECORD_CALL_TARGET); __ Call(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL); PrepareForBailoutForId(expr->ReturnId(), TOS_REG); context()->Plug(rax); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 2a14262257..a8536a1841 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -3880,7 +3880,7 @@ void LCodeGen::DoCallNew(LCallNew* instr) { __ Set(rax, instr->arity()); // No cell in ebx for construct type feedback in optimized code __ LoadRoot(rbx, Heap::kUndefinedValueRootIndex); - CallConstructStub stub(NO_CALL_CONSTRUCTOR_FLAGS); + CallConstructStub stub(NO_CALL_FUNCTION_FLAGS); CallCode(stub.GetCode(isolate()), RelocInfo::CONSTRUCT_CALL, instr); }