Commit 4453164d authored by zhengxing.li's avatar zhengxing.li Committed by Commit bot

X87: [stubs] Introduce a dedicated FastNewObjectStub.

  port ba2077aa (r34136)

  original commit message:
  Move the already existing fast case for %NewObject into a dedicated
  FastNewObjectStub that we can utilize in places where we would otherwise
  fallback to %NewObject immediately, which is rather expensive.

  Also use FastNewObjectStub as the generic implementation of JSCreate,
  which should make constructor inlining based on SharedFunctionInfo (w/o
  specializing to a concrete closure) viable soon.

BUG=

Review URL: https://codereview.chromium.org/1717203002

Cr-Commit-Position: refs/heads/master@{#34182}
parent 4e316c38
......@@ -139,148 +139,20 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ push(eax);
if (create_implicit_receiver) {
__ push(edi);
__ push(edx);
// Try to allocate the object without transitioning into C code. If any of
// the preconditions is not met, the code bails out to the runtime call.
Label rt_call, allocated;
if (FLAG_inline_new) {
// Verify that the new target is a JSFunction.
__ CmpObjectType(edx, JS_FUNCTION_TYPE, ebx);
__ j(not_equal, &rt_call);
// Load the initial map and verify that it is in fact a map.
// edx: new target
__ mov(eax,
FieldOperand(edx, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi
__ JumpIfSmi(eax, &rt_call);
// edi: constructor
// eax: initial map (if proven valid below)
__ CmpObjectType(eax, MAP_TYPE, ebx);
__ j(not_equal, &rt_call);
// Fall back to runtime if the expected base constructor and base
// constructor differ.
__ cmp(edi, FieldOperand(eax, Map::kConstructorOrBackPointerOffset));
__ j(not_equal, &rt_call);
// Check that the constructor is not constructing a JSFunction (see
// comments in Runtime_NewObject in runtime.cc). In which case the
// initial map's instance type would be JS_FUNCTION_TYPE.
// edi: constructor
// eax: initial map
__ CmpInstanceType(eax, JS_FUNCTION_TYPE);
__ j(equal, &rt_call);
// Now allocate the JSObject on the heap.
// edi: constructor
// eax: initial map
__ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset));
__ shl(edi, kPointerSizeLog2);
__ Allocate(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS);
Factory* factory = masm->isolate()->factory();
// Allocated the JSObject, now initialize the fields.
// eax: initial map
// ebx: JSObject (not HeapObject tagged - the actual address).
// edi: start of next object
__ mov(Operand(ebx, JSObject::kMapOffset), eax);
__ mov(ecx, factory->empty_fixed_array());
__ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx);
__ mov(Operand(ebx, JSObject::kElementsOffset), ecx);
__ lea(ecx, Operand(ebx, JSObject::kHeaderSize));
// Add the object tag to make the JSObject real, so that we can continue
// and jump into the continuation code at any time from now on.
__ or_(ebx, Immediate(kHeapObjectTag));
// Fill all the in-object properties with the appropriate filler.
// ebx: JSObject (tagged)
// ecx: First in-object property of JSObject (not tagged)
__ mov(edx, factory->undefined_value());
if (!is_api_function) {
Label no_inobject_slack_tracking;
// The code below relies on these assumptions.
STATIC_ASSERT(Map::kNoSlackTracking == 0);
STATIC_ASSERT(Map::ConstructionCounter::kNext == 32);
// Check if slack tracking is enabled.
__ mov(esi, FieldOperand(eax, Map::kBitField3Offset));
__ shr(esi, Map::ConstructionCounter::kShift);
__ j(zero, &no_inobject_slack_tracking); // Map::kNoSlackTracking
__ push(esi); // Save allocation count value.
// Decrease generous allocation count.
__ sub(FieldOperand(eax, Map::kBitField3Offset),
Immediate(1 << Map::ConstructionCounter::kShift));
// Allocate object with a slack.
__ movzx_b(esi, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
__ neg(esi);
__ lea(esi, Operand(edi, esi, times_pointer_size, 0));
// esi: offset of first field after pre-allocated fields
if (FLAG_debug_code) {
__ cmp(ecx, esi);
__ Assert(less_equal,
kUnexpectedNumberOfPreAllocatedPropertyFields);
}
__ InitializeFieldsWithFiller(ecx, esi, edx);
// To allow truncation fill the remaining fields with one pointer
// filler map.
__ mov(edx, factory->one_pointer_filler_map());
__ InitializeFieldsWithFiller(ecx, edi, edx);
__ pop(esi); // Restore allocation count value before decreasing.
__ cmp(esi, Map::kSlackTrackingCounterEnd);
__ j(not_equal, &allocated);
// Push the object to the stack, and then the initial map as
// an argument to the runtime call.
__ push(ebx);
__ push(eax); // initial map
__ CallRuntime(Runtime::kFinalizeInstanceSize);
__ pop(ebx);
// Continue with JSObject being successfully allocated
// ebx: JSObject (tagged)
__ jmp(&allocated);
__ bind(&no_inobject_slack_tracking);
}
__ InitializeFieldsWithFiller(ecx, edi, edx);
// Continue with JSObject being successfully allocated
// ebx: JSObject (tagged)
__ jmp(&allocated);
}
// Allocate the new receiver object using the runtime call.
// edx: new target
__ bind(&rt_call);
int offset = kPointerSize;
// Must restore esi (context) and edi (constructor) before calling
// runtime.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
__ mov(edi, Operand(esp, offset));
__ push(edi); // constructor function
__ push(edx); // new target
__ CallRuntime(Runtime::kNewObject);
__ mov(ebx, eax); // store result in ebx
// New object allocated.
// ebx: newly allocated object
__ bind(&allocated);
// Allocate the new receiver object.
__ Push(edi);
__ Push(edx);
FastNewObjectStub stub(masm->isolate());
__ CallStub(&stub);
__ mov(ebx, eax);
__ Pop(edx);
__ Pop(edi);
// Restore the parameters.
__ pop(edx); // new.target
__ pop(edi); // Constructor function.
// ----------- S t a t e -------------
// -- edi: constructor function
// -- ebx: newly allocated object
// -- edx: new target
// -----------------------------------
// Retrieve smi-tagged arguments count from the stack.
__ mov(eax, Operand(esp, 0));
......@@ -1630,9 +1502,8 @@ void Builtins::Generate_NumberConstructor_ConstructStub(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(ebx); // the first argument
__ Push(edi); // constructor function
__ Push(edx); // new target
__ CallRuntime(Runtime::kNewObject);
FastNewObjectStub stub(masm->isolate());
__ CallStub(&stub);
__ Pop(FieldOperand(eax, JSValue::kValueOffset));
}
__ Ret();
......@@ -1764,9 +1635,8 @@ void Builtins::Generate_StringConstructor_ConstructStub(MacroAssembler* masm) {
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(ebx); // the first argument
__ Push(edi); // constructor function
__ Push(edx); // new target
__ CallRuntime(Runtime::kNewObject);
FastNewObjectStub stub(masm->isolate());
__ CallStub(&stub);
__ Pop(FieldOperand(eax, JSValue::kValueOffset));
}
__ Ret();
......
......@@ -4401,6 +4401,142 @@ void InternalArrayConstructorStub::Generate(MacroAssembler* masm) {
GenerateCase(masm, FAST_ELEMENTS);
}
void FastNewObjectStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- edi : target
// -- edx : new target
// -- esi : context
// -- esp[0] : return address
// -----------------------------------
__ AssertFunction(edi);
__ AssertReceiver(edx);
// Verify that the new target is a JSFunction.
Label new_object;
__ CmpObjectType(edx, JS_FUNCTION_TYPE, ebx);
__ j(not_equal, &new_object);
// Load the initial map and verify that it's in fact a map.
__ mov(ecx, FieldOperand(edx, JSFunction::kPrototypeOrInitialMapOffset));
__ JumpIfSmi(ecx, &new_object);
__ CmpObjectType(ecx, MAP_TYPE, ebx);
__ j(not_equal, &new_object);
// Fall back to runtime if the target differs from the new target's
// initial map constructor.
__ cmp(edi, FieldOperand(ecx, Map::kConstructorOrBackPointerOffset));
__ j(not_equal, &new_object);
// Allocate the JSObject on the heap.
Label allocate, done_allocate;
__ movzx_b(ebx, FieldOperand(ecx, Map::kInstanceSizeOffset));
__ lea(ebx, Operand(ebx, times_pointer_size, 0));
__ Allocate(ebx, eax, edi, no_reg, &allocate, NO_ALLOCATION_FLAGS);
__ bind(&done_allocate);
// Initialize the JSObject fields.
__ mov(Operand(eax, JSObject::kMapOffset), ecx);
__ mov(Operand(eax, JSObject::kPropertiesOffset),
masm->isolate()->factory()->empty_fixed_array());
__ mov(Operand(eax, JSObject::kElementsOffset),
masm->isolate()->factory()->empty_fixed_array());
STATIC_ASSERT(JSObject::kHeaderSize == 3 * kPointerSize);
__ lea(ebx, Operand(eax, JSObject::kHeaderSize));
// ----------- S t a t e -------------
// -- eax : result (untagged)
// -- ebx : result fields (untagged)
// -- edi : result end (untagged)
// -- ecx : initial map
// -- esi : context
// -- esp[0] : return address
// -----------------------------------
// Perform in-object slack tracking if requested.
Label slack_tracking;
STATIC_ASSERT(Map::kNoSlackTracking == 0);
__ test(FieldOperand(ecx, Map::kBitField3Offset),
Immediate(Map::ConstructionCounter::kMask));
__ j(not_zero, &slack_tracking, Label::kNear);
{
// Initialize all in-object fields with undefined.
__ LoadRoot(edx, Heap::kUndefinedValueRootIndex);
__ InitializeFieldsWithFiller(ebx, edi, edx);
// Add the object tag to make the JSObject real.
STATIC_ASSERT(kHeapObjectTag == 1);
__ inc(eax);
__ Ret();
}
__ bind(&slack_tracking);
{
// Decrease generous allocation count.
STATIC_ASSERT(Map::ConstructionCounter::kNext == 32);
__ sub(FieldOperand(ecx, Map::kBitField3Offset),
Immediate(1 << Map::ConstructionCounter::kShift));
// Initialize the in-object fields with undefined.
__ movzx_b(edx, FieldOperand(ecx, Map::kUnusedPropertyFieldsOffset));
__ neg(edx);
__ lea(edx, Operand(edi, edx, times_pointer_size, 0));
__ LoadRoot(edi, Heap::kUndefinedValueRootIndex);
__ InitializeFieldsWithFiller(ebx, edx, edi);
// Initialize the remaining (reserved) fields with one pointer filler map.
__ movzx_b(edx, FieldOperand(ecx, Map::kUnusedPropertyFieldsOffset));
__ lea(edx, Operand(ebx, edx, times_pointer_size, 0));
__ LoadRoot(edi, Heap::kOnePointerFillerMapRootIndex);
__ InitializeFieldsWithFiller(ebx, edx, edi);
// Add the object tag to make the JSObject real.
STATIC_ASSERT(kHeapObjectTag == 1);
__ inc(eax);
// Check if we can finalize the instance size.
Label finalize;
STATIC_ASSERT(Map::kSlackTrackingCounterEnd == 1);
__ test(FieldOperand(ecx, Map::kBitField3Offset),
Immediate(Map::ConstructionCounter::kMask));
__ j(zero, &finalize, Label::kNear);
__ Ret();
// Finalize the instance size.
__ bind(&finalize);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ Push(eax);
__ Push(ecx);
__ CallRuntime(Runtime::kFinalizeInstanceSize);
__ Pop(eax);
}
__ Ret();
}
// Fall back to %AllocateInNewSpace.
__ bind(&allocate);
{
FrameScope scope(masm, StackFrame::INTERNAL);
__ SmiTag(ebx);
__ Push(ecx);
__ Push(ebx);
__ CallRuntime(Runtime::kAllocateInNewSpace);
__ Pop(ecx);
}
STATIC_ASSERT(kHeapObjectTag == 1);
__ dec(eax);
__ movzx_b(ebx, FieldOperand(ecx, Map::kInstanceSizeOffset));
__ lea(edi, Operand(eax, ebx, times_pointer_size, 0));
__ jmp(&done_allocate);
// Fall back to %NewObject.
__ bind(&new_object);
__ PopReturnAddressTo(ecx);
__ Push(edi);
__ Push(edx);
__ PushReturnAddressFrom(ecx);
__ TailCallRuntime(Runtime::kNewObject);
}
void FastNewRestParameterStub::Generate(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- edi : function
......
......@@ -87,6 +87,12 @@ void FastNewContextDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers, NULL);
}
void FastNewObjectDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {edi, edx};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void FastNewRestParameterDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {edi};
......
......@@ -900,6 +900,17 @@ void MacroAssembler::AssertBoundFunction(Register object) {
}
}
void MacroAssembler::AssertReceiver(Register object) {
if (emit_debug_code()) {
test(object, Immediate(kSmiTagMask));
Check(not_equal, kOperandIsASmiAndNotAReceiver);
Push(object);
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
CmpObjectType(object, FIRST_JS_RECEIVER_TYPE, object);
Pop(object);
Check(above_equal, kOperandIsNotAReceiver);
}
}
void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
if (emit_debug_code()) {
......
......@@ -529,6 +529,9 @@ class MacroAssembler: public Assembler {
// enabled via --debug-code.
void AssertBoundFunction(Register object);
// Abort execution if argument is not a JSReceiver, enabled via --debug-code.
void AssertReceiver(Register object);
// Abort execution if argument is not undefined or an AllocationSite, enabled
// via --debug-code.
void AssertUndefinedOrAllocationSite(Register object);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment