| Index: runtime/vm/stub_code_x64.cc
|
| ===================================================================
|
| --- runtime/vm/stub_code_x64.cc (revision 2556)
|
| +++ runtime/vm/stub_code_x64.cc (working copy)
|
| @@ -5,13 +5,24 @@
|
| #include "vm/globals.h"
|
| #if defined(TARGET_ARCH_X64)
|
|
|
| -#include "vm/native_entry.h"
|
| +#include "vm/code_generator.h"
|
| +#include "vm/compiler.h"
|
| +#include "vm/ic_data.h"
|
| +#include "vm/object_store.h"
|
| +#include "vm/pages.h"
|
| +#include "vm/resolver.h"
|
| +#include "vm/scavenger.h"
|
| #include "vm/stub_code.h"
|
|
|
| +
|
| #define __ assembler->
|
|
|
| namespace dart {
|
|
|
| +DEFINE_FLAG(bool, inline_alloc, true, "Inline allocation of objects.");
|
| +DEFINE_FLAG(bool, use_slow_path, false,
|
| + "Set to true for debugging & verifying the slow paths.");
|
| +
|
| // Input parameters:
|
| // RSP : points to return address.
|
| // RSP + 8 : address of last argument in argument array.
|
| @@ -19,7 +30,9 @@
|
| // RSP + 8*R10 + 8 : address of return value.
|
| // RBX : address of the runtime function to call.
|
| // R10 : number of arguments to the call.
|
| +// Must preserve callee saved registers R12 and R13.
|
| static void GenerateCallRuntimeStub(Assembler* assembler) {
|
| + ASSERT((R12 != CTX) && (R13 != CTX));
|
| const intptr_t isolate_offset = NativeArguments::isolate_offset();
|
| const intptr_t argc_offset = NativeArguments::argc_offset();
|
| const intptr_t argv_offset = NativeArguments::argv_offset();
|
| @@ -81,6 +94,7 @@
|
| // RSP + 8*R10 + 8 : address of return value.
|
| // RBX : address of the runtime function to call.
|
| // R10 : number of arguments to the call.
|
| +// Must preserve callee saved registers R12 and R13.
|
| void StubCode::GenerateDartCallToRuntimeStub(Assembler* assembler) {
|
| GenerateCallRuntimeStub(assembler);
|
| }
|
| @@ -93,6 +107,7 @@
|
| // RSP + 8*R10 + 8 : address of return value.
|
| // RBX : address of the runtime function to call.
|
| // R10 : number of arguments to the call.
|
| +// Must preserve callee saved registers R12 and R13.
|
| void StubCode::GenerateStubCallToRuntimeStub(Assembler* assembler) {
|
| GenerateCallRuntimeStub(assembler);
|
| }
|
| @@ -105,6 +120,7 @@
|
| // RAX - 8*R10 + 8 : address of last argument in argument array.
|
| // RBX : address of the native function to call.
|
| // R10 : number of arguments to the call.
|
| +// Uses R8.
|
| void StubCode::GenerateCallNativeCFunctionStub(Assembler* assembler) {
|
| const intptr_t native_args_struct_offset = 0;
|
| const intptr_t isolate_offset =
|
| @@ -167,8 +183,49 @@
|
| }
|
|
|
|
|
| +// Input parameters:
|
| +// RBX: function object.
|
| +// R10: arguments descriptor array (num_args is first Smi element).
|
| void StubCode::GenerateCallStaticFunctionStub(Assembler* assembler) {
|
| - __ Unimplemented("CallStaticFunction stub");
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| +
|
| + __ movq(RAX, FieldAddress(RBX, Function::code_offset()));
|
| + __ cmpq(RAX, raw_null);
|
| + Label function_compiled;
|
| + __ j(NOT_EQUAL, &function_compiled, Assembler::kNearJump);
|
| +
|
| + // Create a stub frame as we are pushing some objects on the stack before
|
| + // calling into the runtime.
|
| + __ EnterFrame(0);
|
| +
|
| + __ pushq(R10); // Preserve arguments descriptor array.
|
| + __ pushq(RBX);
|
| + __ CallRuntimeFromStub(kCompileFunctionRuntimeEntry);
|
| + __ popq(RBX); // Restore read-only function object argument in RBX.
|
| + __ popq(R10); // Restore arguments descriptor array.
|
| + // Restore RAX.
|
| + __ movq(RAX, FieldAddress(RBX, Function::code_offset()));
|
| +
|
| + // Remove the stub frame as we are about to jump to the dart function.
|
| + __ LeaveFrame();
|
| +
|
| + __ Bind(&function_compiled);
|
| + // Patch caller.
|
| + __ EnterFrame(0);
|
| +
|
| + __ pushq(R10); // Preserve arguments descriptor array.
|
| + __ pushq(RBX); // Preserve function object.
|
| + __ CallRuntimeFromStub(kPatchStaticCallRuntimeEntry);
|
| + __ popq(RBX); // Restore function object argument in RBX.
|
| + __ popq(R10); // Restore arguments descriptor array.
|
| + // Remove the stub frame as we are about to jump to the dart function.
|
| + __ LeaveFrame();
|
| + __ movq(RAX, FieldAddress(RBX, Function::code_offset()));
|
| +
|
| + __ movq(RBX, FieldAddress(RAX, Code::instructions_offset()));
|
| + __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
| + __ jmp(RBX);
|
| }
|
|
|
|
|
| @@ -182,8 +239,312 @@
|
| }
|
|
|
|
|
| +// Lookup for [function-name, arg count] in 'functions_map_'.
|
| +// Input parameters (to be treated as read only, unless calling to target!):
|
| +// RBX: ic-data array.
|
| +// R10: arguments descriptor array (num_args is first Smi element).
|
| +// Stack: return address, arguments.
|
| +// If the lookup succeeds we jump to the target method from here, otherwise
|
| +// we continue in code generated by the caller of 'MegamorphicLookup'.
|
| +static void MegamorphicLookup(Assembler* assembler) {
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| + Label class_in_rax, smi_receiver, null_receiver, not_found;
|
| + // Total number of args is the first Smi in args descriptor array (R10).
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ movq(RAX, Address(RSP, RAX, TIMES_4, 0)); // Get receiver. RAX is a Smi.
|
| + // TODO(srdjan): Remove the special casing below for null receiver, once
|
| + // NullClass is implemented.
|
| + __ cmpq(RAX, raw_null);
|
| + // Use Object class if receiver is null.
|
| + __ j(EQUAL, &null_receiver, Assembler::kNearJump);
|
| + __ testq(RAX, Immediate(kSmiTagMask));
|
| + __ j(ZERO, &smi_receiver, Assembler::kNearJump);
|
| + __ movq(RAX, FieldAddress(RAX, Object::class_offset()));
|
| + __ jmp(&class_in_rax, Assembler::kNearJump);
|
| + __ Bind(&smi_receiver);
|
| + // For Smis we need to get the class from the isolate.
|
| + // Load current Isolate pointer from Context structure into RAX.
|
| + __ movq(RAX, FieldAddress(CTX, Context::isolate_offset()));
|
| + __ movq(RAX, Address(RAX, Isolate::object_store_offset()));
|
| + __ movq(RAX, Address(RAX, ObjectStore::smi_class_offset()));
|
| + __ jmp(&class_in_rax, Assembler::kNearJump);
|
| + __ Bind(&null_receiver);
|
| + __ movq(RAX, FieldAddress(CTX, Context::isolate_offset()));
|
| + __ movq(RAX, Address(RAX, Isolate::object_store_offset()));
|
| + __ movq(RAX, Address(RAX, ObjectStore::object_class_offset()));
|
| +
|
| + __ Bind(&class_in_rax);
|
| + // Class is in RAX.
|
| +
|
| + Label loop, next_iteration;
|
| + // Get functions_cache, since it is allocated lazily it maybe null.
|
| + __ movq(RAX, FieldAddress(RAX, Class::functions_cache_offset()));
|
| + // Iterate and search for identical name.
|
| + __ leaq(R12, FieldAddress(RAX, Array::data_offset()));
|
| +
|
| + // R12 is pointing into content of functions_map_ array.
|
| + __ Bind(&loop);
|
| + __ movq(R13, Address(R12, FunctionsCache::kFunctionName * kWordSize));
|
| +
|
| + __ cmpq(R13, raw_null);
|
| + __ j(EQUAL, ¬_found, Assembler::kNearJump);
|
| +
|
| + ASSERT(ICData::kNameIndex == 0);
|
| + __ cmpq(R13, FieldAddress(RBX, Array::data_offset()));
|
| + __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
|
| +
|
| + // Name found, check total argument count and named argument count.
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + // RAX is total argument count as Smi.
|
| + __ movq(R13, Address(R12, FunctionsCache::kArgCount * kWordSize));
|
| + __ cmpq(RAX, R13); // Compare total argument counts.
|
| + __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
|
| + __ subq(RAX, FieldAddress(R10, Array::data_offset() + kWordSize));
|
| + // RAX is named argument count as Smi.
|
| + __ movq(R13, Address(R12, FunctionsCache::kNamedArgCount * kWordSize));
|
| + __ cmpq(RAX, R13); // Compare named argument counts.
|
| + __ j(NOT_EQUAL, &next_iteration, Assembler::kNearJump);
|
| +
|
| + // Argument count matches, jump to target.
|
| + // R10: arguments descriptor array.
|
| + __ movq(RBX, Address(R12, FunctionsCache::kFunction * kWordSize));
|
| + __ movq(RBX, FieldAddress(RBX, Function::code_offset()));
|
| + __ movq(RBX, FieldAddress(RBX, Code::instructions_offset()));
|
| + __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
| + __ jmp(RBX);
|
| +
|
| + __ Bind(&next_iteration);
|
| + __ AddImmediate(R12, Immediate(FunctionsCache::kNumEntries * kWordSize));
|
| + __ jmp(&loop, Assembler::kNearJump);
|
| +
|
| + __ Bind(¬_found);
|
| +}
|
| +
|
| +
|
| +// Input parameters:
|
| +// R13: argument count, may be zero.
|
| +// Uses RAX, RBX, R10, R12.
|
| +static void PushArgumentsArray(Assembler* assembler, intptr_t arg_offset) {
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| +
|
| + // Allocate array to store arguments of caller.
|
| + __ movq(R10, R13); // Arguments array length.
|
| + __ SmiTag(R10); // Convert to Smi.
|
| + __ movq(RBX, raw_null); // Null element type for raw Array.
|
| + __ call(&StubCode::AllocateArrayLabel());
|
| + __ SmiUntag(R10);
|
| + // RAX: newly allocated array.
|
| + // R10: length of the array (was preserved by the stub).
|
| + __ pushq(RAX); // Array is in RAX and on top of stack.
|
| + __ leaq(R12, Address(RSP, R10, TIMES_8, arg_offset)); // Addr of first arg.
|
| + __ leaq(RBX, FieldAddress(RAX, Array::data_offset()));
|
| + Label loop, loop_condition;
|
| + __ jmp(&loop_condition, Assembler::kNearJump);
|
| + __ Bind(&loop);
|
| + __ movq(RAX, Address(R12, 0));
|
| + __ movq(Address(RBX, 0), RAX);
|
| + __ AddImmediate(RBX, Immediate(kWordSize));
|
| + __ AddImmediate(R12, Immediate(-kWordSize));
|
| + __ Bind(&loop_condition);
|
| + __ decq(R10);
|
| + __ j(POSITIVE, &loop, Assembler::kNearJump);
|
| +}
|
| +
|
| +
|
| +// Input parameters:
|
| +// RBX: ic-data array.
|
| +// R10: arguments descriptor array (num_args is first Smi element).
|
| +// Note: The receiver object is the first argument to the function being
|
| +// called, the stub accesses the receiver from this location directly
|
| +// when trying to resolve the call.
|
| +// Uses R13.
|
| void StubCode::GenerateMegamorphicLookupStub(Assembler* assembler) {
|
| - __ Unimplemented("MegamorphicLookup stub");
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| +
|
| + MegamorphicLookup(assembler);
|
| + // Lookup in function_table_ failed, resolve, compile and enter function
|
| + // into function_table_.
|
| +
|
| + // Create a stub frame as we are pushing some objects on the stack before
|
| + // calling into the runtime.
|
| + __ EnterFrame(0);
|
| +
|
| + // Preserve values across call to resolving.
|
| + // Stack at this point:
|
| + // TOS + 0: Saved RBP of previous frame. <== RBP
|
| + // TOS + 1: Dart code return address
|
| + // TOS + 2: Last argument of caller.
|
| + // ....
|
| + // Total number of args is the first Smi in args descriptor array (R10).
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ movq(RAX, Address(RSP, RAX, TIMES_4, kWordSize)); // Get receiver.
|
| + __ pushq(R10); // Preserve arguments descriptor array.
|
| + __ pushq(RAX); // Preserve receiver.
|
| + __ pushq(RBX); // Preserve ic-data array.
|
| + // First resolve the function to get the function object.
|
| +
|
| + // Setup space for return value on stack by pushing smi 0.
|
| + __ pushq(Immediate(0));
|
| + __ pushq(RAX); // Push receiver.
|
| + __ CallRuntimeFromStub(kResolveCompileInstanceFunctionRuntimeEntry);
|
| + __ popq(RAX); // Remove receiver pushed earlier.
|
| + __ popq(RBX); // Pop returned code object into RBX.
|
| + // Pop preserved values
|
| + __ popq(R10); // Restore ic-data array.
|
| + __ popq(RAX); // Restore receiver.
|
| + __ popq(R13); // Restore arguments descriptor array.
|
| +
|
| + __ cmpq(RBX, raw_null);
|
| + Label check_implicit_closure;
|
| + __ j(EQUAL, &check_implicit_closure, Assembler::kNearJump);
|
| +
|
| + // Remove the stub frame as we are about to jump to the dart function.
|
| + __ LeaveFrame();
|
| +
|
| + __ movq(R10, R13);
|
| + __ movq(RBX, FieldAddress(RBX, Code::instructions_offset()));
|
| + __ addq(RBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
| + __ jmp(RBX);
|
| +
|
| + __ Bind(&check_implicit_closure);
|
| + // RAX: receiver.
|
| + // R10: ic-data array.
|
| + // RBX: raw_null.
|
| + // R13: arguments descriptor array.
|
| + // The target function was not found.
|
| + // First check to see if this is a getter function and we are
|
| + // trying to create a closure of an instance function.
|
| + // Push values that need to be preserved across runtime call.
|
| + __ pushq(RAX); // Preserve receiver.
|
| + __ pushq(R10); // Preserve ic-data array.
|
| + __ pushq(R13); // Preserve arguments descriptor array.
|
| +
|
| + __ pushq(Immediate(0));
|
| + __ pushq(RAX); // Push receiver.
|
| + __ pushq(R10); // Ic-data array.
|
| + __ CallRuntimeFromStub(kResolveImplicitClosureFunctionRuntimeEntry);
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RBX); // Get return value into RBX, might be Closure object.
|
| +
|
| + // Pop preserved values.
|
| + __ popq(R13); // Restore arguments descriptor array.
|
| + __ popq(R10); // Restore ic-data array.
|
| + __ popq(RAX); // Restore receiver.
|
| +
|
| + __ cmpq(RBX, raw_null);
|
| + Label check_implicit_closure_through_getter;
|
| + __ j(EQUAL, &check_implicit_closure_through_getter, Assembler::kNearJump);
|
| +
|
| + __ movq(RAX, RBX); // Return value is the closure object.
|
| + // Remove the stub frame as we are about return.
|
| + __ LeaveFrame();
|
| + __ ret();
|
| +
|
| + __ Bind(&check_implicit_closure_through_getter);
|
| + // RAX: receiver.
|
| + // R10: ic-data array.
|
| + // RBX: raw_null.
|
| + // R13: arguments descriptor array.
|
| + // This is not the case of an instance so invoke the getter of the
|
| + // same name and see if we get a closure back which we are then
|
| + // supposed to invoke.
|
| + // Push values that need to be preserved across runtime call.
|
| + __ pushq(RAX); // Preserve receiver.
|
| + __ pushq(R10); // Preserve ic-data array.
|
| + __ pushq(R13); // Preserve arguments descriptor array.
|
| +
|
| + __ pushq(Immediate(0));
|
| + __ pushq(RAX); // Push receiver.
|
| + __ pushq(R10); // Ic-data array.
|
| + __ CallRuntimeFromStub(kResolveImplicitClosureThroughGetterRuntimeEntry);
|
| + __ popq(R10); // Pop argument.
|
| + __ popq(RAX); // Pop argument.
|
| + __ popq(RBX); // get return value into RBX, might be Closure object.
|
| +
|
| + // Pop preserved values.
|
| + __ popq(R13); // Restore arguments descriptor array.
|
| + __ popq(R10); // Restore ic-data array.
|
| + __ popq(RAX); // Restore receiver.
|
| +
|
| + __ cmpq(RBX, raw_null);
|
| + Label function_not_found;
|
| + __ j(EQUAL, &function_not_found, Assembler::kNearJump);
|
| +
|
| + // RBX: Closure object.
|
| + // R13: Arguments descriptor array.
|
| + __ pushq(Immediate(0)); // Result from invoking Closure.
|
| + __ pushq(RBX); // Closure object.
|
| + __ pushq(R13); // Arguments descriptor.
|
| + __ movq(R13, FieldAddress(R13, Array::data_offset()));
|
| + __ SmiUntag(R13);
|
| + __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver.
|
| + PushArgumentsArray(assembler, (kWordSize * 5));
|
| + // Stack layout explaining "(kWordSize * 5)" offset.
|
| + // TOS + 0: Argument array.
|
| + // TOS + 1: Arguments descriptor array.
|
| + // TOS + 2: Closure object.
|
| + // TOS + 3: Place for result from closure function.
|
| + // TOS + 4: Saved RBP of previous frame. <== RBP
|
| + // TOS + 5: Dart code return address
|
| + // TOS + 6: Last argument of caller.
|
| + // ....
|
| +
|
| + __ CallRuntimeFromStub(kInvokeImplicitClosureFunctionRuntimeEntry);
|
| + // Remove arguments.
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RAX); // Get result into RAX.
|
| +
|
| + // Remove the stub frame as we are about to return.
|
| + __ LeaveFrame();
|
| + __ ret();
|
| +
|
| + __ Bind(&function_not_found);
|
| + // The target function was not found, so invoke method
|
| + // "void noSuchMethod(function_name, args_array)".
|
| + // RAX: receiver.
|
| + // R10: ic-data array.
|
| + // RBX: raw_null.
|
| + // R13: argument descriptor array.
|
| +
|
| + // Setup space for return value on stack by pushing smi 0.
|
| + __ pushq(Immediate(0)); // Result from noSuchMethod.
|
| + __ pushq(RAX); // Receiver.
|
| + __ pushq(R10); // IC-data array.
|
| + __ pushq(R13); // Argument descriptor array.
|
| + __ movq(R13, FieldAddress(R13, Array::data_offset()));
|
| + __ SmiUntag(R13);
|
| + __ subq(R13, Immediate(1)); // Arguments array length, minus the receiver.
|
| + // See stack layout below explaining "wordSize * 6" offset.
|
| + PushArgumentsArray(assembler, (kWordSize * 6));
|
| +
|
| + // Stack:
|
| + // TOS + 0: Argument array.
|
| + // TOS + 1: Argument descriptor array.
|
| + // TOS + 2: IC-data array.
|
| + // TOS + 3: Receiver.
|
| + // TOS + 4: Place for result from noSuchMethod.
|
| + // TOS + 5: Saved RBP of previous frame. <== RBP
|
| + // TOS + 6: Dart code return address
|
| + // TOS + 7: Last argument of caller.
|
| + // ....
|
| +
|
| + __ CallRuntimeFromStub(kInvokeNoSuchMethodFunctionRuntimeEntry);
|
| + // Remove arguments.
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RAX);
|
| + __ popq(RAX); // Get result into RAX.
|
| +
|
| + // Remove the stub frame as we are about to return.
|
| + __ LeaveFrame();
|
| + __ ret();
|
| }
|
|
|
|
|
| @@ -192,8 +553,115 @@
|
| }
|
|
|
|
|
| +// Called for inline allocation of arrays.
|
| +// Input parameters:
|
| +// R10 : Array length as Smi.
|
| +// RBX : array element type (either NULL or an instantiated type).
|
| +// NOTE: R10 cannot be clobbered here as the caller relies on it being saved.
|
| +// The newly allocated object is returned in RAX.
|
| void StubCode::GenerateAllocateArrayStub(Assembler* assembler) {
|
| - __ Unimplemented("AllocateArray stub");
|
| + Label slow_case;
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| +
|
| + if (FLAG_inline_alloc) {
|
| + // Compute the size to be allocated, it is based on the array length
|
| + // and it computed as:
|
| + // RoundedAllocationSize((array_length * kwordSize) + sizeof(RawArray)).
|
| + // Assert that length is a Smi.
|
| + __ testq(R10, Immediate(kSmiTagSize));
|
| + if (FLAG_use_slow_path) {
|
| + __ jmp(&slow_case);
|
| + } else {
|
| + __ j(NOT_ZERO, &slow_case, Assembler::kNearJump);
|
| + }
|
| + __ movq(R13, FieldAddress(CTX, Context::isolate_offset()));
|
| + __ movq(R13, Address(R13, Isolate::heap_offset()));
|
| + __ movq(R13, Address(R13, Heap::new_space_offset()));
|
| +
|
| + // Calculate and align allocation size.
|
| + // Load new object start and calculate next object start.
|
| + // RBX: array element type.
|
| + // R10: Array length as Smi.
|
| + // R13: Points to new space object.
|
| + __ movq(RAX, Address(R13, Scavenger::top_offset()));
|
| + intptr_t fixed_size = sizeof(RawArray) + kObjectAlignment - 1;
|
| + __ leaq(R12, Address(R10, TIMES_4, fixed_size)); // R10 is Smi.
|
| + ASSERT(kSmiTagShift == 1);
|
| + __ andq(R12, Immediate(-kObjectAlignment));
|
| + __ leaq(R12, Address(RAX, R12, TIMES_1, 0));
|
| +
|
| + // Check if the allocation fits into the remaining space.
|
| + // RAX: potential new object start.
|
| + // R12: potential next object start.
|
| + // RBX: array element type.
|
| + // R10: Array length as Smi.
|
| + // R13: Points to new space object.
|
| + __ cmpq(R12, Address(R13, Scavenger::end_offset()));
|
| + __ j(ABOVE_EQUAL, &slow_case, Assembler::kNearJump);
|
| +
|
| + // Successfully allocated the object(s), now update top to point to
|
| + // next object start and initialize the object.
|
| + // RAX: potential new object start.
|
| + // R12: potential next object start.
|
| + // R13: Points to new space object.
|
| + __ movq(Address(R13, Scavenger::top_offset()), R12);
|
| + __ addq(RAX, Immediate(kHeapObjectTag));
|
| +
|
| + // RAX: new object start as a tagged pointer.
|
| + // R12: new object end address.
|
| + // RBX: array element type.
|
| + // R10: Array length as Smi.
|
| +
|
| + // Store the type argument field.
|
| + __ movq(FieldAddress(RAX, Array::type_arguments_offset()), RBX);
|
| +
|
| + // Set the length field.
|
| + __ movq(FieldAddress(RAX, Array::length_offset()), R10);
|
| +
|
| + // Store class value for array.
|
| + __ movq(RBX, FieldAddress(CTX, Context::isolate_offset()));
|
| + __ movq(RBX, Address(RBX, Isolate::object_store_offset()));
|
| + __ movq(RBX, Address(RBX, ObjectStore::array_class_offset()));
|
| + __ movq(FieldAddress(RAX, Array::class_offset()), RBX);
|
| + __ movq(FieldAddress(RAX, Array::tags_offset()), Immediate(0)); // Tags.
|
| +
|
| + // Initialize all array elements to raw_null.
|
| + // RAX: new object start as a tagged pointer.
|
| + // R12: new object end address.
|
| + // RBX: iterator which initially points to the start of the variable
|
| + // data area to be initialized.
|
| + __ leaq(RBX, FieldAddress(RAX, Array::data_offset()));
|
| + Label done;
|
| + Label init_loop;
|
| + __ Bind(&init_loop);
|
| + __ cmpq(RBX, R12);
|
| + __ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
|
| + __ movq(Address(RBX, 0), raw_null);
|
| + __ addq(RBX, Immediate(kWordSize));
|
| + __ jmp(&init_loop, Assembler::kNearJump);
|
| + __ Bind(&done);
|
| +
|
| + // Done allocating and initializing the array.
|
| + // RAX: new object.
|
| + __ ret();
|
| + }
|
| +
|
| + // Unable to allocate the array using the fast inline code, just call
|
| + // into the runtime.
|
| + __ Bind(&slow_case);
|
| + __ EnterFrame(0);
|
| + __ pushq(raw_null); // Push Null object for return value.
|
| + __ pushq(R10); // Array length as Smi.
|
| + __ pushq(RBX); // Element type.
|
| + __ pushq(raw_null); // Null instantiator.
|
| + __ CallRuntimeFromStub(kAllocateArrayRuntimeEntry);
|
| + __ popq(RAX); // Pop instantiator.
|
| + __ popq(RAX); // Pop element type argument.
|
| + __ popq(R10); // Pop array length argument.
|
| + __ popq(RAX); // Pop return value from return slot.
|
| + __ LeaveFrame();
|
| + __ ret();
|
| }
|
|
|
|
|
| @@ -323,9 +791,197 @@
|
| }
|
|
|
|
|
| +
|
| +
|
| +// Called for inline allocation of objects.
|
| +// Input parameters:
|
| +// RSP + 16 : type arguments object (only if class is parameterized).
|
| +// RSP + 8 : type arguments of instantiator (only if class is parameterized).
|
| +// RSP : points to return address.
|
| +// Uses RAX, RBX, RCX, RDX, RDI as temporary registers.
|
| void StubCode::GenerateAllocationStubForClass(Assembler* assembler,
|
| const Class& cls) {
|
| - __ Unimplemented("AllocateObject stub");
|
| + const intptr_t kObjectTypeArgumentsOffset = 2 * kWordSize;
|
| + const intptr_t kInstantiatorTypeArgumentsOffset = 1 * kWordSize;
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| + // The generated code is different if the class is parameterized.
|
| + const bool is_cls_parameterized =
|
| + cls.type_arguments_instance_field_offset() != Class::kNoTypeArguments;
|
| + // kInlineInstanceSize is a constant used as a threshold for determining
|
| + // when the object initialization should be done as a loop or as
|
| + // straight line code.
|
| + const int kInlineInstanceSize = 12; // In words.
|
| + const intptr_t instance_size = cls.instance_size();
|
| + ASSERT(instance_size > 0);
|
| + const intptr_t type_args_size = InstantiatedTypeArguments::InstanceSize();
|
| + if (FLAG_inline_alloc &&
|
| + PageSpace::IsPageAllocatableSize(instance_size + type_args_size)) {
|
| + Label slow_case;
|
| + Heap* heap = Isolate::Current()->heap();
|
| + __ movq(RAX, Immediate(heap->TopAddress()));
|
| + __ movq(RAX, Address(RAX, 0));
|
| + __ leaq(RBX, Address(RAX, instance_size));
|
| + if (is_cls_parameterized) {
|
| + __ movq(RCX, RBX);
|
| + // A new InstantiatedTypeArguments object only needs to be allocated if
|
| + // the instantiator is non-null.
|
| + Label null_instantiator;
|
| + __ cmpq(Address(RSP, kInstantiatorTypeArgumentsOffset), raw_null);
|
| + __ j(EQUAL, &null_instantiator, Assembler::kNearJump);
|
| + __ addq(RBX, Immediate(type_args_size));
|
| + __ Bind(&null_instantiator);
|
| + // RCX: potential new object end and, if RCX != RBX, potential new
|
| + // InstantiatedTypeArguments object start.
|
| + }
|
| + // Check if the allocation fits into the remaining space.
|
| + // RAX: potential new object start.
|
| + // RBX: potential next object start.
|
| + __ movq(RDI, Immediate(heap->EndAddress()));
|
| + __ cmpq(RBX, Address(RDI, 0));
|
| + if (FLAG_use_slow_path) {
|
| + __ jmp(&slow_case);
|
| + } else {
|
| + __ j(ABOVE_EQUAL, &slow_case, Assembler::kNearJump);
|
| + }
|
| +
|
| + // Successfully allocated the object(s), now update top to point to
|
| + // next object start and initialize the object.
|
| + __ movq(RDI, Immediate(heap->TopAddress()));
|
| + __ movq(Address(RDI, 0), RBX);
|
| +
|
| + if (is_cls_parameterized) {
|
| + // Initialize the type arguments field in the object.
|
| + // RAX: new object start.
|
| + // RCX: potential new object end and, if RCX != RBX, potential new
|
| + // InstantiatedTypeArguments object start.
|
| + // RBX: next object start.
|
| + Label type_arguments_ready;
|
| + __ movq(RDI, Address(RSP, kObjectTypeArgumentsOffset));
|
| + __ cmpq(RCX, RBX);
|
| + __ j(EQUAL, &type_arguments_ready, Assembler::kNearJump);
|
| + // Initialize InstantiatedTypeArguments object at RCX.
|
| + __ movq(Address(RCX,
|
| + InstantiatedTypeArguments::uninstantiated_type_arguments_offset()),
|
| + RDI);
|
| + __ movq(RDX, Address(RSP, kInstantiatorTypeArgumentsOffset));
|
| + __ movq(Address(RCX,
|
| + InstantiatedTypeArguments::instantiator_type_arguments_offset()),
|
| + RDX);
|
| + __ LoadObject(RDX,
|
| + Class::ZoneHandle(Object::instantiated_type_arguments_class()));
|
| + __ movq(Address(RCX, Instance::class_offset()), RDX); // Set its class.
|
| + __ movq(Address(RCX, Instance::tags_offset()), Immediate(0)); // Tags.
|
| + // Set the new InstantiatedTypeArguments object (RCX) as the type
|
| + // arguments (RDI) of the new object (RAX).
|
| + __ movq(RDI, RCX);
|
| + __ addq(RDI, Immediate(kHeapObjectTag));
|
| + // Set RBX to new object end.
|
| + __ movq(RBX, RCX);
|
| + __ Bind(&type_arguments_ready);
|
| + // RAX: new object.
|
| + // RDI: new object type arguments.
|
| + }
|
| +
|
| + // Initialize the class field in the object.
|
| + // RAX: new object start.
|
| + // RBX: next object start.
|
| + // RDI: new object type arguments (if is_cls_parameterized).
|
| + __ LoadObject(RDX, cls); // Load class of object to be allocated.
|
| + __ movq(Address(RAX, Instance::class_offset()), RDX);
|
| + __ movq(Address(RAX, Instance::tags_offset()), Immediate(0)); // Tags.
|
| +
|
| + // Initialize the remaining words of the object.
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| +
|
| + // RAX: new object start.
|
| + // RBX: next object start.
|
| + // RDX: class of the object to be allocated.
|
| + // First try inlining the initialization without a loop.
|
| + if (instance_size < (kInlineInstanceSize * kWordSize) &&
|
| + cls.num_native_fields() == 0) {
|
| + // Check if the object contains any non-header fields.
|
| + // Small objects are initialized using a consecutive set of writes.
|
| + for (intptr_t current_offset = sizeof(RawObject);
|
| + current_offset < instance_size;
|
| + current_offset += kWordSize) {
|
| + __ movq(Address(RAX, current_offset), raw_null);
|
| + }
|
| + } else {
|
| + __ leaq(RCX, Address(RAX, sizeof(RawObject)));
|
| + // Loop until the whole object is initialized.
|
| + Label init_loop;
|
| + if (cls.num_native_fields() > 0) {
|
| + // Initialize native fields.
|
| + // RAX: new object.
|
| + // RBX: next object start.
|
| + // RDX: class of the object to be allocated.
|
| + // RCX: next word to be initialized.
|
| + intptr_t offset = Class::num_native_fields_offset() - kHeapObjectTag;
|
| + __ movq(RDX, Address(RDX, offset));
|
| + __ leaq(RDX, Address(RAX, RDX, TIMES_8, sizeof(RawObject)));
|
| +
|
| + // RDX: start of dart fields.
|
| + // RCX: next word to be initialized.
|
| + Label init_native_loop;
|
| + __ Bind(&init_native_loop);
|
| + __ cmpq(RCX, RDX);
|
| + __ j(ABOVE_EQUAL, &init_loop, Assembler::kNearJump);
|
| + __ movq(Address(RCX, 0), Immediate(0));
|
| + __ addq(RCX, Immediate(kWordSize));
|
| + __ jmp(&init_native_loop, Assembler::kNearJump);
|
| + }
|
| + // Now initialize the dart fields.
|
| + // RAX: new object.
|
| + // RBX: next object start.
|
| + // RCX: next word to be initialized.
|
| + Label done;
|
| + __ Bind(&init_loop);
|
| + __ cmpq(RCX, RBX);
|
| + __ j(ABOVE_EQUAL, &done, Assembler::kNearJump);
|
| + __ movq(Address(RCX, 0), raw_null);
|
| + __ addq(RCX, Immediate(kWordSize));
|
| + __ jmp(&init_loop, Assembler::kNearJump);
|
| + __ Bind(&done);
|
| + }
|
| + if (is_cls_parameterized) {
|
| + // RDI: new object type arguments.
|
| + // Set the type arguments in the new object.
|
| + __ movq(Address(RAX, cls.type_arguments_instance_field_offset()), RDI);
|
| + }
|
| + // Done allocating and initializing the instance.
|
| + // RAX: new object.
|
| + __ addq(RAX, Immediate(kHeapObjectTag));
|
| + __ ret();
|
| +
|
| + __ Bind(&slow_case);
|
| + }
|
| + if (is_cls_parameterized) {
|
| + __ movq(RAX, Address(RSP, kObjectTypeArgumentsOffset));
|
| + __ movq(RDX, Address(RSP, kInstantiatorTypeArgumentsOffset));
|
| + }
|
| + // Create a stub frame.
|
| + __ EnterFrame(0);
|
| + const Object& new_object = Object::ZoneHandle();
|
| + __ PushObject(new_object); // Push Null object for return value.
|
| + __ PushObject(cls); // Push class of object to be allocated.
|
| + if (is_cls_parameterized) {
|
| + __ pushq(RAX); // Push type arguments of object to be allocated.
|
| + __ pushq(RDX); // Push type arguments of instantiator.
|
| + } else {
|
| + __ pushq(raw_null); // Push null type arguments.
|
| + __ pushq(raw_null); // Push null instantiator.
|
| + }
|
| + __ CallRuntimeFromStub(kAllocateObjectRuntimeEntry); // Allocate object.
|
| + __ popq(RAX); // Pop argument (instantiator).
|
| + __ popq(RAX); // Pop argument (type arguments of object).
|
| + __ popq(RAX); // Pop argument (class of object).
|
| + __ popq(RAX); // Pop result (newly allocated object).
|
| + // RAX: new object
|
| + // Restore the frame pointer.
|
| + __ LeaveFrame();
|
| + __ ret();
|
| }
|
|
|
|
|
| @@ -340,13 +996,165 @@
|
| }
|
|
|
|
|
| +// Generate inline cache check for 'num_args'.
|
| +// RBX: Inline cache data array.
|
| +// R10: Arguments array.
|
| +// TOS(0): return address
|
| +// Control flow:
|
| +// - If receiver is null -> jump to IC miss.
|
| +// - If receiver is Smi -> load Smi class.
|
| +// - If receiver is not-Smi -> load receiver's class.
|
| +// - Check if 'num_args' (including receiver) match any IC data group.
|
| +// - Match found -> jump to target.
|
| +// - Match not found -> jump to IC miss.
|
| +void StubCode::GenerateNArgsCheckInlineCacheStub(Assembler* assembler,
|
| + intptr_t num_args) {
|
| + ASSERT(num_args > 0);
|
| + // Get receiver.
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ movq(RAX, Address(RSP, RAX, TIMES_4, 0)); // RAX is Smi.
|
| +
|
| + Label get_class, ic_miss;
|
| + __ call(&get_class);
|
| + // RAX: receiver's class
|
| + // RBX: IC data array.
|
| +
|
| +#if defined(DEBUG)
|
| + { Label ok;
|
| + // Check that the IC data array has NumberOfArgumentsChecked() == num_args.
|
| + __ movq(RCX, FieldAddress(RBX,
|
| + Array::data_offset() + ICData::kNumArgsCheckedIndex * kWordSize));
|
| + const Immediate value =
|
| + Immediate(reinterpret_cast<int64_t>(Smi::New(num_args)));
|
| + __ cmpq(RCX, value);
|
| + __ j(EQUAL, &ok, Assembler::kNearJump);
|
| + __ Stop("Incorrect stub for IC data");
|
| + __ Bind(&ok);
|
| + }
|
| +#endif // DEBUG
|
| +
|
| + // Loop that checks if there is an IC data match.
|
| + // RAX: receiver's class.
|
| + // RBX: IC data array (preserved).
|
| + __ leaq(R12, FieldAddress(RBX,
|
| + Array::data_offset() + ICData::kChecksStartIndex * kWordSize));
|
| + // R12: pointing to a class to check against (into IC data array).
|
| + const Immediate raw_null =
|
| + Immediate(reinterpret_cast<intptr_t>(Object::null()));
|
| + Label loop, found;
|
| + if (num_args == 1) {
|
| + __ Bind(&loop);
|
| + __ movq(R13, Address(R12, 0)); // Get class to check.
|
| + __ cmpq(RAX, R13); // Match?
|
| + __ j(EQUAL, &found, Assembler::kNearJump);
|
| + __ addq(R12, Immediate(kWordSize * 2)); // Next element (class + target).
|
| + __ cmpq(R13, raw_null); // Done?
|
| + __ j(NOT_EQUAL, &loop, Assembler::kNearJump);
|
| + } else if (num_args == 2) {
|
| + Label no_match;
|
| + __ Bind(&loop);
|
| + __ movq(R13, Address(R12, 0)); // Get class from IC data to check.
|
| + // Get receiver.
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ movq(RAX, Address(RSP, RAX, TIMES_4, 0)); // RAX is Smi.
|
| + __ call(&get_class);
|
| + __ cmpq(RAX, R13); // Match?
|
| + __ j(NOT_EQUAL, &no_match, Assembler::kNearJump);
|
| + // Check second.
|
| + __ movq(R13, Address(R12, kWordSize)); // Get class from IC data to check.
|
| + // Get next argument.
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ movq(RAX, Address(RSP, RAX, TIMES_4, -kWordSize)); // RAX is Smi.
|
| + __ call(&get_class);
|
| + __ cmpq(RAX, R13); // Match?
|
| + __ j(EQUAL, &found, Assembler::kNearJump);
|
| + __ Bind(&no_match);
|
| + __ addq(R12, Immediate(kWordSize * (1 + num_args))); // Next element.
|
| + __ cmpq(R13, raw_null); // Done?
|
| + __ j(NOT_EQUAL, &loop, Assembler::kNearJump);
|
| + }
|
| +
|
| + __ Bind(&ic_miss);
|
| + // Get receiver, again.
|
| + __ movq(RAX, FieldAddress(R10, Array::data_offset()));
|
| + __ leaq(RAX, Address(RSP, RAX, TIMES_4, 0)); // RAX is Smi.
|
| + __ EnterFrame(0);
|
| + // Setup space for return value on stack by pushing smi 0.
|
| + __ pushq(R10); // Preserve arguments array.
|
| + __ pushq(RBX); // Preserve IC data array
|
| + __ pushq(Immediate(0)); // Space for result (target code object).
|
| + __ movq(R10, FieldAddress(R10, Array::data_offset()));
|
| + // Push call arguments.
|
| + for (intptr_t i = 0; i < num_args; i++) {
|
| + __ movq(R10, Address(RAX, -kWordSize * i));
|
| + __ pushq(R10);
|
| + }
|
| + if (num_args == 1) {
|
| + __ CallRuntimeFromStub(kInlineCacheMissHandlerOneArgRuntimeEntry);
|
| + } else if (num_args == 2) {
|
| + __ CallRuntimeFromStub(kInlineCacheMissHandlerTwoArgsRuntimeEntry);
|
| + } else {
|
| + UNIMPLEMENTED();
|
| + }
|
| + // Remove call arguments pushed earlier.
|
| + for (intptr_t i = 0; i < num_args; i++) {
|
| + __ popq(RAX);
|
| + }
|
| + __ popq(RAX); // Pop returned code object into RAX (null if not found).
|
| + __ popq(RBX); // Restore IC data array.
|
| + __ popq(R10); // Restore arguments array.
|
| + __ LeaveFrame();
|
| + Label call_target_function;
|
| + __ cmpq(RAX, raw_null);
|
| + __ j(NOT_EQUAL, &call_target_function, Assembler::kNearJump);
|
| + // NoSuchMethod or closure.
|
| + __ jmp(&StubCode::MegamorphicLookupLabel());
|
| +
|
| + __ Bind(&found);
|
| + // R12: Pointer to an IC data check group (classes + target)
|
| + __ movq(RAX, Address(R12, kWordSize * num_args)); // Target function.
|
| +
|
| + __ Bind(&call_target_function);
|
| + // RAX: Target function.
|
| + __ movq(RAX, FieldAddress(RAX, Function::code_offset()));
|
| + __ movq(RAX, FieldAddress(RAX, Code::instructions_offset()));
|
| + __ addq(RAX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
|
| + __ jmp(RAX);
|
| +
|
| + __ Bind(&get_class);
|
| + Label not_smi;
|
| + // Test if Smi -> load Smi class for comparison.
|
| + __ testq(RAX, Immediate(kSmiTagMask));
|
| + __ j(NOT_ZERO, ¬_smi, Assembler::kNearJump);
|
| + const Class& smi_class =
|
| + Class::ZoneHandle(Isolate::Current()->object_store()->smi_class());
|
| + __ LoadObject(RAX, smi_class);
|
| + __ ret();
|
| +
|
| + __ Bind(¬_smi);
|
| + __ movq(RAX, FieldAddress(RAX, Object::class_offset()));
|
| + __ ret();
|
| +}
|
| +
|
| +
|
| +// Use inline cache data array to invoke the target or continue in inline
|
| +// cache miss handler. Stub for 1-argument check (receiver class).
|
| +// RCX: Inline cache data array
|
| +// RDX: Arguments array
|
| +// TOS(0): return address
|
| +// Inline cache data array structure:
|
| +// 0: function-name
|
| +// 1: N, number of arguments checked.
|
| +// 2 .. (length - 1): group of checks, each check containing:
|
| +// - N classes.
|
| +// - 1 target function.
|
| void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) {
|
| - __ Unimplemented("GenerateOneArgCheckInlineCacheStub stub");
|
| + return GenerateNArgsCheckInlineCacheStub(assembler, 1);
|
| }
|
|
|
|
|
| void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) {
|
| - __ Unimplemented("GenerateTwoArgsCheckInlineCacheStub stub");
|
| + return GenerateNArgsCheckInlineCacheStub(assembler, 2);
|
| }
|
|
|
|
|
|
|