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); |
} |