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);
}
-static void GenericCallHelper(MacroAssembler* masm,
- const CallIC::State& state) {
+void CallFunctionStub::Generate(MacroAssembler* masm) {
// r1 : the function to call
+ // 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);
// 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));
__ b(ne, &cont);
}
- if (state.IsSloppy()) {
- // 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 (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);
{
// 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() && state.IsSloppy()) {
+ if (CallAsMethod()) {
__ bind(&wrap);
-
- if (!state.IsGeneric()) {
- __ 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));
- }
-}
-
-
void CallConstructStub::Generate(MacroAssembler* masm) {
// r0 : number of arguments
// r1 : the function to call
}
-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;
}
-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 -------------
}
+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
}
+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 -------------
// Code common for calls using the IC.
-void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
+void FullCodeGenerator::EmitCallWithIC(Call* expr) {
Expression* callee = expr->expression();
+ ZoneList<Expression*>* 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);
// 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());
__ 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<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
// Load the function from the receiver.
ASSERT(callee->IsProperty());
__ 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<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(args->at(i));
}
}
-
- // Record source position of the IC call.
+ // Record source position for debugger.
SetSourcePosition(expr->position());
- Handle<Code> ic = CallIC::initialize_stub(
- isolate(), arg_count, call_type);
+
Handle<Object> 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));
__ 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).
// 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);
__ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
__ push(r1);
// Emit function call.
- EmitCall(expr);
+ EmitCallWithStub(expr);
}
#ifdef DEBUG
__ 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);
__ 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);
}
// 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);
}
-static void GenericCallHelper(MacroAssembler* masm,
- const CallIC::State& state) {
+void CallFunctionStub::Generate(MacroAssembler* masm) {
+ ASM_LOCATION("CallFunctionStub::Generate");
// x1 function the function to call
+ // 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));
__ Tbnz(w4, SharedFunctionInfo::kNative, &cont);
}
- if (state.IsSloppy()) {
- // Compute the receiver in sloppy mode.
- __ Peek(x3, argc * kPointerSize);
+ // Compute the receiver in sloppy mode.
+ __ Peek(x3, argc_ * kPointerSize);
+ 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);
{
// 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() && state.IsSloppy()) {
+ if (CallAsMethod()) {
__ Bind(&wrap);
-
- if (!state.IsGeneric()) {
- __ 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));
- }
-}
-
-
void CallConstructStub::Generate(MacroAssembler* masm) {
ASM_LOCATION("CallConstructStub::Generate");
// x0 : number of arguments
}
-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_);
}
-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 -------------
}
+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
}
+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 -------------
// Code common for calls using the IC.
-void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
- Expression* callee = expr->expression();
+void FullCodeGenerator::EmitCallWithIC(Call* expr) {
+ ASM_LOCATION("EmitCallWithIC");
- CallIC::CallType call_type = callee->IsVariableProxy()
- ? CallIC::FUNCTION
- : CallIC::METHOD;
+ Expression* callee = expr->expression();
+ ZoneList<Expression*>* 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);
// 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());
// Push the target function under the receiver.
__ Pop(x10);
__ Push(x0, x10);
+ flags = CALL_AS_METHOD;
+ }
+
+ // Load the arguments.
+ { PreservePositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
}
- EmitCall(expr, call_type);
+ // 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<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
// Load the function from the receiver.
ASSERT(callee->IsProperty());
__ 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<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(args->at(i));
}
}
- // Record source position of the IC call.
+ // Record source position for debugger.
SetSourcePosition(expr->position());
- Handle<Code> ic = CallIC::initialize_stub(
- isolate(), arg_count, call_type);
Handle<Object> 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));
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).
// 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 {
__ LoadRoot(x1, Heap::kUndefinedValueRootIndex);
__ Push(x1);
// Emit function call.
- EmitCall(expr);
+ EmitCallWithStub(expr);
}
#ifdef DEBUG
__ 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);
// 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));
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;
}
#ifdef ENABLE_DEBUGGER_SUPPORT
-static void Generate_CallICStub_DebugBreak(MacroAssembler* masm) {
- Debug::GenerateCallICStubDebugBreak(masm);
-}
-
-
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateLoadICDebugBreak(masm);
}
}
+static void Generate_CallFunctionStub_Recording_DebugBreak(
+ MacroAssembler* masm) {
+ Debug::GenerateCallFunctionStubRecordDebugBreak(masm);
+}
+
+
static void Generate_CallConstructStub_DebugBreak(MacroAssembler* masm) {
Debug::GenerateCallConstructStubDebugBreak(masm);
}
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, \
}
-void CallICStub::PrintState(StringStream* stream) {
- state_.Print(stream);
-}
-
-
void InstanceofStub::PrintName(StringStream* stream) {
const char* args = "";
if (HasArgsInRegisters()) {
void CallFunctionStub::PrintName(StringStream* stream) {
stream->Add("CallFunctionStub_Args%d", argc_);
+ if (RecordCallTarget()) stream->Add("_Recording");
}
V(CompareIC) \
V(CompareNilIC) \
V(MathPow) \
- V(CallIC) \
V(FunctionPrototype) \
V(RecordWrite) \
V(StoreBufferOverflow) \
};
-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) { }
void Generate(MacroAssembler* masm);
+ virtual void FinishCode(Handle<Code> code) {
+ code->set_has_function_cache(RecordCallTarget());
+ }
+
static int ExtractArgcFromMinorKey(int minor_key) {
return ArgcBits::decode(minor_key);
}
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;
}
class CallConstructStub: public PlatformCodeStub {
public:
- explicit CallConstructStub(CallConstructorFlags flags) : flags_(flags) {}
+ explicit CallConstructStub(CallFunctionFlags flags) : flags_(flags) {}
void Generate(MacroAssembler* masm);
}
private:
- CallConstructorFlags flags_;
+ CallFunctionFlags flags_;
virtual void PrintName(StringStream* stream);
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;
}
};
if (target_code->kind() == Code::STUB) {
return target_code->major_key() == CodeStub::CallFunction;
}
- return target_code->is_call_stub();
}
return false;
}
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;
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<Code>(maybe_call_function_stub);
} 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
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):
// 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();
}
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();
// 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);
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);
// 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.
// 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
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);
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);
}
-static void GenericCallHelper(MacroAssembler* masm,
- const CallIC::State& state) {
+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
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),
__ j(not_equal, &cont);
}
- if (state.IsSloppy()) {
- // 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 (NeedsChecks()) {
__ JumpIfSmi(eax, &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);
{
// 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<Code> adaptor = isolate->builtins()->ArgumentsAdaptorTrampoline();
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
- if (state.CallAsMethod() && state.IsSloppy()) {
+ if (CallAsMethod()) {
__ bind(&wrap);
-
- if (!state.IsGeneric()) {
- // 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);
__ 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));
- }
-}
-
-
void CallConstructStub::Generate(MacroAssembler* masm) {
// eax : number of arguments
// ebx : feedback vector
}
-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;
}
}
-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 -------------
}
+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 -------------
}
+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
}
+
+
// Code common for calls using the IC.
-void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
+void FullCodeGenerator::EmitCallWithIC(Call* expr) {
Expression* callee = expr->expression();
+ ZoneList<Expression*>* 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);
// 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());
// 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<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
// Load the function from the receiver.
ASSERT(callee->IsProperty());
__ push(Operand(esp, 0));
__ mov(Operand(esp, kPointerSize), eax);
- 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 of the IC call.
+ SetSourcePosition(expr->position());
+ CallFunctionStub stub(arg_count, CALL_AS_METHOD);
+ __ 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::EmitCall(Call* expr, CallIC::CallType call_type) {
- // Load the arguments.
+void FullCodeGenerator::EmitCallWithStub(Call* expr) {
+ // Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(args->at(i));
}
}
-
- // Record source position of the IC call.
+ // Record source position for debugger.
SetSourcePosition(expr->position());
- Handle<Code> ic = CallIC::initialize_stub(
- isolate(), arg_count, call_type);
+
Handle<Object> 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));
- // 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.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
-
context()->DropAndPlug(1, eax);
}
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).
// 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();
VisitForStackValue(property->obj());
}
if (property->key()->IsPropertyName()) {
- EmitCallWithLoadIC(expr);
+ EmitCallWithIC(expr);
} else {
- EmitKeyedCallWithLoadIC(expr, property->key());
+ EmitKeyedCallWithIC(expr, property->key());
}
} else {
}
__ push(Immediate(isolate()->factory()->undefined_value()));
// Emit function call.
- EmitCall(expr);
+ EmitCallWithStub(expr);
}
#ifdef DEBUG
__ 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);
// 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);
}
}
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),
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 =
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:
}
-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,
}
-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<Code> CallIC::initialize_stub(Isolate* isolate,
- int argc,
- CallType call_type) {
- CallICStub stub(State::DefaultCallState(argc, call_type));
- Handle<Code> code = stub.GetCode(isolate);
- return code;
-}
-
-
Handle<Code> StoreIC::initialize_stub(Isolate* isolate,
StrictMode strict_mode) {
ExtraICState extra_state = ComputeExtraICState(strict_mode);
}
-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<JSFunction> 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<Object> receiver,
- Handle<Object> function,
- Handle<FixedArray> vector,
- Handle<Smi> 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<JSFunction> js_function = Handle<JSFunction>::cast(function);
- State new_state = state.ToMonomorphicState(js_function);
- if (new_state != state) {
- CallICStub stub(new_state);
- set_target(*stub.GetCode(isolate()));
- TRACE_IC("CallIC", Handle<Object>(js_function->shared()->name(),
- isolate()));
- }
- }
-}
-
-
#undef TRACE_IC
//
// Used from ic-<arch>.cc.
-RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) {
- HandleScope scope(isolate);
- ASSERT(args.length() == 4);
- CallIC ic(isolate);
- Handle<Object> receiver = args.at<Object>(0);
- Handle<Object> function = args.at<Object>(1);
- Handle<FixedArray> vector = args.at<FixedArray>(2);
- Handle<Smi> slot = args.at<Smi>(3);
- ic.HandleMiss(receiver, function, vector, slot);
- return *function;
-}
-
-
// Used from ic-<arch>.cc.
RUNTIME_FUNCTION(MaybeObject*, LoadIC_Miss) {
HandleScope scope(isolate);
#define IC_UTIL_LIST(ICU) \
ICU(LoadIC_Miss) \
ICU(KeyedLoadIC_Miss) \
- ICU(CallIC_Miss) \
ICU(StoreIC_Miss) \
ICU(StoreIC_ArrayLength) \
ICU(StoreIC_Slow) \
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.
};
-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<JSFunction> 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<int, 0, Code::kArgumentsBits> {};
- class CallTypeBits: public BitField<CallType, Code::kArgumentsBits, 1> {};
- class StubTypeBits:
- public BitField<StubType, Code::kArgumentsBits + 1, 1> {}; // NOLINT
- class ArgumentCheckBits:
- public BitField<ArgumentCheck,
- Code::kArgumentsBits + 2, 1> {}; // NOLINT
- class StrictModeBits:
- public BitField<StrictMode,
- Code::kArgumentsBits + 3, 1> {}; // 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<Object> receiver,
- Handle<Object> function,
- Handle<FixedArray> vector,
- Handle<Smi> slot);
-
- // Code generator routines.
- static Handle<Code> 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
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;
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") \
kind() == LOAD_IC ||
kind() == KEYED_LOAD_IC ||
kind() == STORE_IC ||
- kind() == CALL_IC ||
kind() == KEYED_STORE_IC ||
kind() == TO_BOOLEAN_IC;
}
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();
}
kind() == BINARY_OP_IC ||
kind() == STUB ||
kind() == LOAD_IC ||
- kind() == CALL_IC ||
kind() == KEYED_LOAD_IC ||
kind() == STORE_IC ||
kind() == KEYED_STORE_IC);
// 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());
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);
#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) \
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; }
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.
};
-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).
// 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);
}
-static void GenericCallHelper(MacroAssembler* masm,
- const CallIC::State& state) {
+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
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),
__ j(not_equal, &cont);
}
- if (state.IsSloppy()) {
- // Load the receiver from the stack.
- __ movp(rax, args.GetReceiverOperand());
+
+ // Load the receiver from the stack.
+ __ movp(rax, args.GetReceiverOperand());
+
+ 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);
{
// 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<Code> adaptor =
__ Jump(adaptor, RelocInfo::CODE_TARGET);
}
- if (state.CallAsMethod() && state.IsSloppy()) {
+ if (CallAsMethod()) {
__ bind(&wrap);
-
- if (!state.IsGeneric()) {
- // Do not transform the receiver for natives.
- __ movp(rcx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
- __ testb(FieldOperand(rcx, SharedFunctionInfo::kNativeByteOffset),
- Immediate(1 << SharedFunctionInfo::kNativeBitWithinByte));
- }
-
// Wrap the receiver and patch it back onto the stack.
{ FrameScope frame_scope(masm, StackFrame::INTERNAL);
__ Push(rdi);
}
-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));
- }
-}
-
-
void CallConstructStub::Generate(MacroAssembler* masm) {
// rax : number of arguments
// rbx : feedback vector
}
-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;
}
}
-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 -------------
}
+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 -------------
}
+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
// Code common for calls using the IC.
-void FullCodeGenerator::EmitCallWithLoadIC(Call* expr) {
+void FullCodeGenerator::EmitCallWithIC(Call* expr) {
Expression* callee = expr->expression();
+ ZoneList<Expression*>* 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);
// 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());
// 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<Expression*>* args = expr->arguments();
+ int arg_count = args->length();
// Load the function from the receiver.
ASSERT(callee->IsProperty());
__ 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<Expression*>* args = expr->arguments();
int arg_count = args->length();
{ PreservePositionScope scope(masm()->positions_recorder());
VisitForStackValue(args->at(i));
}
}
-
- // Record source position of the IC call.
+ // Record source position for debugger.
SetSourcePosition(expr->position());
- Handle<Code> ic = CallIC::initialize_stub(
- isolate(), arg_count, call_type);
+
Handle<Object> uninitialized =
TypeFeedbackInfo::UninitializedSentinel(isolate());
StoreFeedbackVectorSlot(expr->CallFeedbackSlot(), uninitialized);
__ Move(rbx, FeedbackVector());
__ Move(rdx, Smi::FromInt(expr->CallFeedbackSlot()));
- __ 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);
+ // Record call targets in unoptimized code.
+ CallFunctionStub stub(arg_count, RECORD_CALL_TARGET);
+ __ movp(rdi, Operand(rsp, (arg_count + 1) * kPointerSize));
+ __ CallStub(&stub);
RecordJSReturnSite(expr);
-
// Restore context register.
__ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// Discard the function left on TOS.
__ 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).
// 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);
}
__ PushRoot(Heap::kUndefinedValueRootIndex);
// Emit function call.
- EmitCall(expr);
+ EmitCallWithStub(expr);
}
#ifdef DEBUG
__ 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);
__ 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);
}